content/base/src/nsDOMMutationObserver.cpp

branch
TOR_BUG_9701
changeset 11
deefc01c0e14
equal deleted inserted replaced
-1:000000000000 0:d657c85a27cb
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/. */
6
7 #include "nsDOMMutationObserver.h"
8
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"
17
18 nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>*
19 nsDOMMutationObserver::sScheduledMutationObservers = nullptr;
20
21 nsDOMMutationObserver* nsDOMMutationObserver::sCurrentObserver = nullptr;
22
23 uint32_t nsDOMMutationObserver::sMutationLevel = 0;
24 uint64_t nsDOMMutationObserver::sCount = 0;
25
26 nsAutoTArray<nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>, 4>*
27 nsDOMMutationObserver::sCurrentlyHandlingObservers = nullptr;
28
29 nsINodeList*
30 nsDOMMutationRecord::AddedNodes()
31 {
32 if (!mAddedNodes) {
33 mAddedNodes = new nsSimpleContentList(mTarget);
34 }
35 return mAddedNodes;
36 }
37
38 nsINodeList*
39 nsDOMMutationRecord::RemovedNodes()
40 {
41 if (!mRemovedNodes) {
42 mRemovedNodes = new nsSimpleContentList(mTarget);
43 }
44 return mRemovedNodes;
45 }
46
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
51
52 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMMutationRecord)
53 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMMutationRecord)
54
55 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_7(nsDOMMutationRecord,
56 mTarget,
57 mPreviousSibling, mNextSibling,
58 mAddedNodes, mRemovedNodes,
59 mNext, mOwner)
60
61 // Observer
62
63 NS_IMPL_ADDREF(nsMutationReceiver)
64 NS_IMPL_RELEASE(nsMutationReceiver)
65
66 NS_INTERFACE_MAP_BEGIN(nsMutationReceiver)
67 NS_INTERFACE_MAP_ENTRY(nsISupports)
68 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
69 NS_INTERFACE_MAP_END
70
71 nsMutationReceiver::nsMutationReceiver(nsINode* aTarget,
72 nsDOMMutationObserver* aObserver)
73 : nsMutationReceiverBase(aTarget, aObserver)
74 {
75 mTarget->BindObject(aObserver);
76 }
77
78 void
79 nsMutationReceiver::Disconnect(bool aRemoveFromObserver)
80 {
81 if (mRegisterTarget) {
82 mRegisterTarget->RemoveMutationObserver(this);
83 mRegisterTarget = nullptr;
84 }
85
86 mParent = nullptr;
87 nsINode* target = mTarget;
88 mTarget = nullptr;
89 nsDOMMutationObserver* observer = mObserver;
90 mObserver = nullptr;
91 RemoveClones();
92
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 }
101
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 }
114
115 nsDOMMutationRecord* m =
116 Observer()->CurrentRecord(nsGkAtoms::attributes);
117
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 }
132
133 if (AttributeOldValue() && m->mPrevValue.IsVoid()) {
134 if (!aElement->GetAttr(aNameSpaceID, aAttribute, m->mPrevValue)) {
135 m->mPrevValue.SetIsVoid(true);
136 }
137 }
138 }
139
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 }
150
151 nsDOMMutationRecord* m =
152 Observer()->CurrentRecord(nsGkAtoms::characterData);
153
154 NS_ASSERTION(!m->mTarget || m->mTarget == aContent,
155 "Wrong target!");
156
157 if (!m->mTarget) {
158 m->mTarget = aContent;
159 }
160 if (CharacterDataOldValue() && m->mPrevValue.IsVoid()) {
161 aContent->GetText()->AppendTo(m->mPrevValue);
162 }
163 }
164
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 }
176
177 if (nsAutoMutationBatch::IsBatching()) {
178 if (parent == nsAutoMutationBatch::GetBatchTarget()) {
179 nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList);
180 }
181 return;
182 }
183
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);
194
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 }
202
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 }
214
215 if (nsAutoMutationBatch::IsBatching()) {
216 if (parent == nsAutoMutationBatch::GetBatchTarget()) {
217 nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList);
218 }
219 return;
220 }
221
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 }
234
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 }
245
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 }
256
257 bool wantsChildList = ChildList() && (Subtree() || parent == Target());
258 if (wantsChildList || Subtree()) {
259 nsAutoMutationBatch::NodeRemoved(aChild);
260 nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList);
261 }
262
263 return;
264 }
265
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 }
292
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 }
310
311 void nsMutationReceiver::NodeWillBeDestroyed(const nsINode *aNode)
312 {
313 NS_ASSERTION(!mParent, "Shouldn't have mParent here!");
314 Disconnect(true);
315 }
316
317 // Observer
318
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
324
325 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMMutationObserver)
326 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMMutationObserver)
327
328 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMMutationObserver)
329
330 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMMutationObserver)
331 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
332 NS_IMPL_CYCLE_COLLECTION_TRACE_END
333
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
345
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
354
355 nsMutationReceiver*
356 nsDOMMutationObserver::GetReceiverFor(nsINode* aNode, bool aMayCreate)
357 {
358 if (!aMayCreate && !aNode->MayHaveDOMMutationObserver()) {
359 return nullptr;
360 }
361
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 }
370
371 nsMutationReceiver* r = new nsMutationReceiver(aNode, this);
372 mReceivers.AppendObject(r);
373 return r;
374 }
375
376 void
377 nsDOMMutationObserver::RemoveReceiver(nsMutationReceiver* aReceiver)
378 {
379 mReceivers.RemoveObject(aReceiver);
380 }
381
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 }
416
417 void
418 nsDOMMutationObserver::ScheduleForRun()
419 {
420 nsDOMMutationObserver::AddCurrentlyHandlingObserver(this);
421
422 if (mWaitingForRun) {
423 return;
424 }
425 mWaitingForRun = true;
426 RescheduleForRun();
427 }
428
429 void
430 nsDOMMutationObserver::RescheduleForRun()
431 {
432 if (!sScheduledMutationObservers) {
433 sScheduledMutationObservers = new nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>;
434 }
435
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 }
449
450 void
451 nsDOMMutationObserver::Observe(nsINode& aTarget,
452 const mozilla::dom::MutationObserverInit& aOptions,
453 mozilla::ErrorResult& aRv)
454 {
455
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 }
468
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();
476
477 if (len != 0 && !aOptions.mAttributes) {
478 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
479 return;
480 }
481 filters.SetCapacity(len);
482
483 for (uint32_t i = 0; i < len; ++i) {
484 nsCOMPtr<nsIAtom> a = do_GetAtom(filtersAsString[i]);
485 filters.AppendObject(a);
486 }
487 }
488
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();
499
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 }
507
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 }
518
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 }
535
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 }
561
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 }
578
579 void
580 nsDOMMutationObserver::HandleMutation()
581 {
582 NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), "Whaat!");
583 NS_ASSERTION(mCurrentMutations.IsEmpty(),
584 "Still generating MutationRecords?");
585
586 mWaitingForRun = false;
587
588 for (int32_t i = 0; i < mReceivers.Count(); ++i) {
589 mReceivers[i]->RemoveClones();
590 }
591 mTransientReceivers.Clear();
592
593 nsPIDOMWindow* outer = mOwner->GetOuterWindow();
594 if (!mPendingMutationCount || !outer ||
595 outer->GetCurrentInnerWindow() != mOwner) {
596 ClearPendingRecords();
597 return;
598 }
599
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();
615
616 mozilla::ErrorResult rv;
617 mCallback->Call(this, mutations, *this, rv);
618 }
619
620 class AsyncMutationHandler : public nsRunnable
621 {
622 public:
623 NS_IMETHOD Run()
624 {
625 nsDOMMutationObserver::HandleMutations();
626 return NS_OK;
627 }
628 };
629
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 }
647
648 nsTArray<nsRefPtr<nsDOMMutationObserver> >* suppressedObservers = nullptr;
649
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 }
669
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 }
680
681 nsDOMMutationRecord*
682 nsDOMMutationObserver::CurrentRecord(nsIAtom* aType)
683 {
684 NS_ASSERTION(sMutationLevel > 0, "Unexpected mutation level!");
685
686 while (mCurrentMutations.Length() < sMutationLevel) {
687 mCurrentMutations.AppendElement(static_cast<nsDOMMutationRecord*>(nullptr));
688 }
689
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 }
697
698 NS_ASSERTION(mCurrentMutations[last]->mType == aType,
699 "Unexpected MutationRecord type!");
700
701 return mCurrentMutations[last];
702 }
703
704 nsDOMMutationObserver::~nsDOMMutationObserver()
705 {
706 for (int32_t i = 0; i < mReceivers.Count(); ++i) {
707 mReceivers[i]->RemoveClones();
708 }
709 }
710
711 void
712 nsDOMMutationObserver::EnterMutationHandling()
713 {
714 ++sMutationLevel;
715 }
716
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 }
740
741 void
742 nsDOMMutationObserver::AddCurrentlyHandlingObserver(nsDOMMutationObserver* aObserver)
743 {
744 NS_ASSERTION(sMutationLevel > 0, "Unexpected mutation level!");
745
746 if (!sCurrentlyHandlingObservers) {
747 sCurrentlyHandlingObservers =
748 new nsAutoTArray<nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>, 4>;
749 }
750
751 while (sCurrentlyHandlingObservers->Length() < sMutationLevel) {
752 sCurrentlyHandlingObservers->InsertElementAt(
753 sCurrentlyHandlingObservers->Length());
754 }
755
756 uint32_t last = sMutationLevel - 1;
757 if (!sCurrentlyHandlingObservers->ElementAt(last).Contains(aObserver)) {
758 sCurrentlyHandlingObservers->ElementAt(last).AppendElement(aObserver);
759 }
760 }
761
762 void
763 nsDOMMutationObserver::Shutdown()
764 {
765 delete sCurrentlyHandlingObservers;
766 sCurrentlyHandlingObservers = nullptr;
767 delete sScheduledMutationObservers;
768 sScheduledMutationObservers = nullptr;
769 }
770
771 nsAutoMutationBatch*
772 nsAutoMutationBatch::sCurrentBatch = nullptr;
773
774 void
775 nsAutoMutationBatch::Done()
776 {
777 if (sCurrentBatch != this) {
778 return;
779 }
780
781 sCurrentBatch = mPreviousBatch;
782 if (mObservers.IsEmpty()) {
783 nsDOMMutationObserver::LeaveMutationHandling();
784 // Nothing to do.
785 return;
786 }
787
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;
792
793 nsRefPtr<nsSimpleContentList> removedList;
794 if (wantsChildList) {
795 removedList = new nsSimpleContentList(mBatchTarget);
796 }
797
798 nsTArray<nsMutationReceiver*> allObservers;
799 ob->GetAllSubtreeObserversFor(mBatchTarget, allObservers);
800
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 }
808
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 }

mercurial