Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 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 : #ifndef nsDOMMutationObserver_h
8 : #define nsDOMMutationObserver_h
9 :
10 : #include "mozilla/Attributes.h"
11 : #include "mozilla/Move.h"
12 : #include "nsCycleCollectionParticipant.h"
13 : #include "nsPIDOMWindow.h"
14 : #include "nsIScriptContext.h"
15 : #include "nsStubAnimationObserver.h"
16 : #include "nsCOMArray.h"
17 : #include "nsTArray.h"
18 : #include "nsIVariant.h"
19 : #include "nsContentList.h"
20 : #include "mozilla/dom/Element.h"
21 : #include "nsClassHashtable.h"
22 : #include "nsNodeUtils.h"
23 : #include "nsIDOMMutationEvent.h"
24 : #include "nsWrapperCache.h"
25 : #include "mozilla/dom/MutationObserverBinding.h"
26 : #include "nsIDocument.h"
27 : #include "mozilla/dom/Animation.h"
28 : #include "nsIAnimationObserver.h"
29 :
30 : class nsDOMMutationObserver;
31 : using mozilla::dom::MutationObservingInfo;
32 :
33 : class nsDOMMutationRecord final : public nsISupports,
34 : public nsWrapperCache
35 : {
36 0 : virtual ~nsDOMMutationRecord() {}
37 :
38 : public:
39 : typedef nsTArray<RefPtr<mozilla::dom::Animation>> AnimationArray;
40 :
41 0 : nsDOMMutationRecord(nsIAtom* aType, nsISupports* aOwner)
42 0 : : mType(aType), mAttrNamespace(NullString()), mPrevValue(NullString()), mOwner(aOwner)
43 : {
44 0 : }
45 :
46 0 : nsISupports* GetParentObject() const
47 : {
48 0 : return mOwner;
49 : }
50 :
51 0 : virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
52 : {
53 0 : return mozilla::dom::MutationRecordBinding::Wrap(aCx, this, aGivenProto);
54 : }
55 :
56 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
57 1 : NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMMutationRecord)
58 :
59 0 : void GetType(mozilla::dom::DOMString& aRetVal) const
60 : {
61 0 : aRetVal.SetOwnedAtom(mType, mozilla::dom::DOMString::eNullNotExpected);
62 0 : }
63 :
64 0 : nsINode* GetTarget() const
65 : {
66 0 : return mTarget;
67 : }
68 :
69 : nsINodeList* AddedNodes();
70 :
71 : nsINodeList* RemovedNodes();
72 :
73 0 : nsINode* GetPreviousSibling() const
74 : {
75 0 : return mPreviousSibling;
76 : }
77 :
78 0 : nsINode* GetNextSibling() const
79 : {
80 0 : return mNextSibling;
81 : }
82 :
83 0 : void GetAttributeName(mozilla::dom::DOMString& aRetVal) const
84 : {
85 0 : aRetVal.SetOwnedAtom(mAttrName, mozilla::dom::DOMString::eTreatNullAsNull);
86 0 : }
87 :
88 0 : void GetAttributeNamespace(mozilla::dom::DOMString& aRetVal) const
89 : {
90 0 : aRetVal.SetOwnedString(mAttrNamespace);
91 0 : }
92 :
93 0 : void GetOldValue(mozilla::dom::DOMString& aRetVal) const
94 : {
95 0 : aRetVal.SetOwnedString(mPrevValue);
96 0 : }
97 :
98 0 : void GetAddedAnimations(AnimationArray& aRetVal) const
99 : {
100 0 : aRetVal = mAddedAnimations;
101 0 : }
102 :
103 0 : void GetRemovedAnimations(AnimationArray& aRetVal) const
104 : {
105 0 : aRetVal = mRemovedAnimations;
106 0 : }
107 :
108 0 : void GetChangedAnimations(AnimationArray& aRetVal) const
109 : {
110 0 : aRetVal = mChangedAnimations;
111 0 : }
112 :
113 : nsCOMPtr<nsINode> mTarget;
114 : nsCOMPtr<nsIAtom> mType;
115 : nsCOMPtr<nsIAtom> mAttrName;
116 : nsString mAttrNamespace;
117 : nsString mPrevValue;
118 : RefPtr<nsSimpleContentList> mAddedNodes;
119 : RefPtr<nsSimpleContentList> mRemovedNodes;
120 : nsCOMPtr<nsINode> mPreviousSibling;
121 : nsCOMPtr<nsINode> mNextSibling;
122 : AnimationArray mAddedAnimations;
123 : AnimationArray mRemovedAnimations;
124 : AnimationArray mChangedAnimations;
125 :
126 : RefPtr<nsDOMMutationRecord> mNext;
127 : nsCOMPtr<nsISupports> mOwner;
128 : };
129 :
130 : // Base class just prevents direct access to
131 : // members to make sure we go through getters/setters.
132 : class nsMutationReceiverBase : public nsStubAnimationObserver
133 : {
134 : public:
135 0 : virtual ~nsMutationReceiverBase() { }
136 :
137 : nsDOMMutationObserver* Observer();
138 1 : nsINode* Target() { return mParent ? mParent->Target() : mTarget; }
139 0 : nsINode* RegisterTarget() { return mRegisterTarget; }
140 :
141 0 : bool Subtree() { return mParent ? mParent->Subtree() : mSubtree; }
142 1 : void SetSubtree(bool aSubtree)
143 : {
144 1 : NS_ASSERTION(!mParent, "Shouldn't have parent");
145 1 : mSubtree = aSubtree;
146 1 : }
147 :
148 0 : bool ChildList() { return mParent ? mParent->ChildList() : mChildList; }
149 1 : void SetChildList(bool aChildList)
150 : {
151 1 : NS_ASSERTION(!mParent, "Shouldn't have parent");
152 1 : mChildList = aChildList;
153 1 : }
154 :
155 0 : bool CharacterData()
156 : {
157 0 : return mParent ? mParent->CharacterData() : mCharacterData;
158 : }
159 1 : void SetCharacterData(bool aCharacterData)
160 : {
161 1 : NS_ASSERTION(!mParent, "Shouldn't have parent");
162 1 : mCharacterData = aCharacterData;
163 1 : }
164 :
165 0 : bool CharacterDataOldValue()
166 : {
167 0 : return mParent ? mParent->CharacterDataOldValue() : mCharacterDataOldValue;
168 : }
169 1 : void SetCharacterDataOldValue(bool aOldValue)
170 : {
171 1 : NS_ASSERTION(!mParent, "Shouldn't have parent");
172 1 : mCharacterDataOldValue = aOldValue;
173 1 : }
174 :
175 0 : bool NativeAnonymousChildList()
176 : {
177 0 : return mParent ? mParent->NativeAnonymousChildList() : mNativeAnonymousChildList;
178 : }
179 1 : void SetNativeAnonymousChildList(bool aOldValue)
180 : {
181 1 : NS_ASSERTION(!mParent, "Shouldn't have parent");
182 1 : mNativeAnonymousChildList = aOldValue;
183 1 : }
184 :
185 0 : bool Attributes() { return mParent ? mParent->Attributes() : mAttributes; }
186 1 : void SetAttributes(bool aAttributes)
187 : {
188 1 : NS_ASSERTION(!mParent, "Shouldn't have parent");
189 1 : mAttributes = aAttributes;
190 1 : }
191 :
192 0 : bool AllAttributes()
193 : {
194 0 : return mParent ? mParent->AllAttributes()
195 0 : : mAllAttributes;
196 : }
197 1 : void SetAllAttributes(bool aAll)
198 : {
199 1 : NS_ASSERTION(!mParent, "Shouldn't have parent");
200 1 : mAllAttributes = aAll;
201 1 : }
202 :
203 0 : bool Animations() { return mParent ? mParent->Animations() : mAnimations; }
204 1 : void SetAnimations(bool aAnimations)
205 : {
206 1 : NS_ASSERTION(!mParent, "Shouldn't have parent");
207 1 : mAnimations = aAnimations;
208 1 : }
209 :
210 0 : bool AttributeOldValue() {
211 0 : return mParent ? mParent->AttributeOldValue()
212 0 : : mAttributeOldValue;
213 : }
214 1 : void SetAttributeOldValue(bool aOldValue)
215 : {
216 1 : NS_ASSERTION(!mParent, "Shouldn't have parent");
217 1 : mAttributeOldValue = aOldValue;
218 1 : }
219 :
220 0 : nsCOMArray<nsIAtom>& AttributeFilter() { return mAttributeFilter; }
221 1 : void SetAttributeFilter(nsCOMArray<nsIAtom>&& aFilter)
222 : {
223 1 : NS_ASSERTION(!mParent, "Shouldn't have parent");
224 1 : mAttributeFilter.Clear();
225 1 : mAttributeFilter = mozilla::Move(aFilter);
226 1 : }
227 :
228 0 : void AddClone(nsMutationReceiverBase* aClone)
229 : {
230 0 : mTransientReceivers.AppendObject(aClone);
231 0 : }
232 :
233 : void RemoveClone(nsMutationReceiverBase* aClone)
234 : {
235 : mTransientReceivers.RemoveObject(aClone);
236 : }
237 :
238 : protected:
239 1 : nsMutationReceiverBase(nsINode* aTarget, nsDOMMutationObserver* aObserver)
240 1 : : mTarget(aTarget)
241 : , mObserver(aObserver)
242 : , mRegisterTarget(aTarget)
243 : , mSubtree(false)
244 : , mChildList(false)
245 : , mCharacterData(false)
246 : , mCharacterDataOldValue(false)
247 : , mNativeAnonymousChildList(false)
248 : , mAttributes(false)
249 : , mAllAttributes(false)
250 : , mAttributeOldValue(false)
251 1 : , mAnimations(false)
252 : {
253 1 : }
254 :
255 0 : nsMutationReceiverBase(nsINode* aRegisterTarget,
256 : nsMutationReceiverBase* aParent)
257 0 : : mTarget(nullptr)
258 : , mObserver(nullptr)
259 : , mParent(aParent)
260 : , mRegisterTarget(aRegisterTarget)
261 : , mKungFuDeathGrip(aParent->Target())
262 : , mSubtree(false)
263 : , mChildList(false)
264 : , mCharacterData(false)
265 : , mCharacterDataOldValue(false)
266 : , mNativeAnonymousChildList(false)
267 : , mAttributes(false)
268 : , mAllAttributes(false)
269 : , mAttributeOldValue(false)
270 0 : , mAnimations(false)
271 : {
272 0 : NS_ASSERTION(mParent->Subtree(), "Should clone a non-subtree observer!");
273 0 : }
274 :
275 : virtual void AddMutationObserver() = 0;
276 :
277 1 : void AddObserver()
278 : {
279 1 : AddMutationObserver();
280 1 : mRegisterTarget->SetMayHaveDOMMutationObserver();
281 1 : mRegisterTarget->OwnerDoc()->SetMayHaveDOMMutationObservers();
282 1 : }
283 :
284 : bool IsObservable(nsIContent* aContent);
285 :
286 0 : bool ObservesAttr(nsINode* aRegisterTarget,
287 : mozilla::dom::Element* aElement,
288 : int32_t aNameSpaceID,
289 : nsIAtom* aAttr)
290 : {
291 0 : if (mParent) {
292 0 : return mParent->ObservesAttr(aRegisterTarget, aElement, aNameSpaceID, aAttr);
293 : }
294 0 : if (!Attributes() ||
295 0 : (!Subtree() && aElement != Target()) ||
296 0 : (Subtree() && aRegisterTarget->SubtreeRoot() != aElement->SubtreeRoot()) ||
297 0 : !IsObservable(aElement)) {
298 0 : return false;
299 : }
300 0 : if (AllAttributes()) {
301 0 : return true;
302 : }
303 :
304 0 : if (aNameSpaceID != kNameSpaceID_None) {
305 0 : return false;
306 : }
307 :
308 0 : nsCOMArray<nsIAtom>& filters = AttributeFilter();
309 0 : for (int32_t i = 0; i < filters.Count(); ++i) {
310 0 : if (filters[i] == aAttr) {
311 0 : return true;
312 : }
313 : }
314 0 : return false;
315 : }
316 :
317 : // The target for the MutationObserver.observe() method.
318 : nsINode* mTarget;
319 : nsDOMMutationObserver* mObserver;
320 : RefPtr<nsMutationReceiverBase> mParent; // Cleared after microtask.
321 : // The node to which Gecko-internal nsIMutationObserver was registered to.
322 : // This is different than mTarget when dealing with transient observers.
323 : nsINode* mRegisterTarget;
324 : nsCOMArray<nsMutationReceiverBase> mTransientReceivers;
325 : // While we have transient receivers, keep the original mutation receiver
326 : // alive so it doesn't go away and disconnect all its transient receivers.
327 : nsCOMPtr<nsINode> mKungFuDeathGrip;
328 :
329 : private:
330 : bool mSubtree;
331 : bool mChildList;
332 : bool mCharacterData;
333 : bool mCharacterDataOldValue;
334 : bool mNativeAnonymousChildList;
335 : bool mAttributes;
336 : bool mAllAttributes;
337 : bool mAttributeOldValue;
338 : bool mAnimations;
339 : nsCOMArray<nsIAtom> mAttributeFilter;
340 : };
341 :
342 :
343 : class nsMutationReceiver : public nsMutationReceiverBase
344 : {
345 : protected:
346 0 : virtual ~nsMutationReceiver() { Disconnect(false); }
347 :
348 : public:
349 1 : static nsMutationReceiver* Create(nsINode* aTarget,
350 : nsDOMMutationObserver* aObserver)
351 : {
352 1 : nsMutationReceiver* r = new nsMutationReceiver(aTarget, aObserver);
353 1 : r->AddObserver();
354 1 : return r;
355 : }
356 :
357 0 : static nsMutationReceiver* Create(nsINode* aRegisterTarget,
358 : nsMutationReceiverBase* aParent)
359 : {
360 0 : nsMutationReceiver* r = new nsMutationReceiver(aRegisterTarget, aParent);
361 0 : aParent->AddClone(r);
362 0 : r->AddObserver();
363 0 : return r;
364 : }
365 :
366 0 : nsMutationReceiver* GetParent()
367 : {
368 0 : return static_cast<nsMutationReceiver*>(mParent.get());
369 : }
370 :
371 1 : void RemoveClones()
372 : {
373 1 : for (int32_t i = 0; i < mTransientReceivers.Count(); ++i) {
374 : nsMutationReceiver* r =
375 0 : static_cast<nsMutationReceiver*>(mTransientReceivers[i]);
376 0 : r->DisconnectTransientReceiver();
377 : }
378 1 : mTransientReceivers.Clear();
379 1 : }
380 :
381 0 : void DisconnectTransientReceiver()
382 : {
383 0 : if (mRegisterTarget) {
384 0 : mRegisterTarget->RemoveMutationObserver(this);
385 0 : mRegisterTarget = nullptr;
386 : }
387 :
388 0 : mParent = nullptr;
389 0 : NS_ASSERTION(!mTarget, "Should not have mTarget");
390 0 : NS_ASSERTION(!mObserver, "Should not have mObserver");
391 0 : }
392 :
393 : void Disconnect(bool aRemoveFromObserver);
394 :
395 : NS_DECL_ISUPPORTS
396 :
397 : NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
398 : NS_DECL_NSIMUTATIONOBSERVER_NATIVEANONYMOUSCHILDLISTCHANGE
399 : NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATAWILLCHANGE
400 : NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
401 : NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
402 : NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
403 : NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
404 :
405 0 : virtual void AttributeSetToCurrentValue(nsIDocument* aDocument,
406 : mozilla::dom::Element* aElement,
407 : int32_t aNameSpaceID,
408 : nsIAtom* aAttribute) override
409 : {
410 : // We can reuse AttributeWillChange implementation.
411 : AttributeWillChange(aDocument, aElement, aNameSpaceID, aAttribute,
412 0 : nsIDOMMutationEvent::MODIFICATION, nullptr);
413 0 : }
414 :
415 : protected:
416 : nsMutationReceiver(nsINode* aTarget, nsDOMMutationObserver* aObserver);
417 :
418 0 : nsMutationReceiver(nsINode* aRegisterTarget, nsMutationReceiverBase* aParent)
419 0 : : nsMutationReceiverBase(aRegisterTarget, aParent)
420 : {
421 0 : NS_ASSERTION(!static_cast<nsMutationReceiver*>(aParent)->GetParent(),
422 : "Shouldn't create deep observer hierarchies!");
423 0 : }
424 :
425 1 : virtual void AddMutationObserver() override
426 : {
427 1 : mRegisterTarget->AddMutationObserver(this);
428 1 : }
429 : };
430 :
431 : class nsAnimationReceiver : public nsMutationReceiver
432 : {
433 : public:
434 0 : static nsAnimationReceiver* Create(nsINode* aTarget,
435 : nsDOMMutationObserver* aObserver)
436 : {
437 0 : nsAnimationReceiver* r = new nsAnimationReceiver(aTarget, aObserver);
438 0 : r->AddObserver();
439 0 : return r;
440 : }
441 :
442 0 : static nsAnimationReceiver* Create(nsINode* aRegisterTarget,
443 : nsMutationReceiverBase* aParent)
444 : {
445 0 : nsAnimationReceiver* r = new nsAnimationReceiver(aRegisterTarget, aParent);
446 0 : aParent->AddClone(r);
447 0 : r->AddObserver();
448 0 : return r;
449 : }
450 :
451 : NS_DECL_ISUPPORTS_INHERITED
452 :
453 : NS_DECL_NSIANIMATIONOBSERVER_ANIMATIONADDED
454 : NS_DECL_NSIANIMATIONOBSERVER_ANIMATIONCHANGED
455 : NS_DECL_NSIANIMATIONOBSERVER_ANIMATIONREMOVED
456 :
457 : protected:
458 0 : virtual ~nsAnimationReceiver() {}
459 :
460 0 : nsAnimationReceiver(nsINode* aTarget, nsDOMMutationObserver* aObserver)
461 0 : : nsMutationReceiver(aTarget, aObserver) {}
462 :
463 0 : nsAnimationReceiver(nsINode* aRegisterTarget, nsMutationReceiverBase* aParent)
464 0 : : nsMutationReceiver(aRegisterTarget, aParent) {}
465 :
466 0 : virtual void AddMutationObserver() override
467 : {
468 0 : mRegisterTarget->AddAnimationObserver(this);
469 0 : }
470 :
471 : private:
472 : enum AnimationMutation {
473 : eAnimationMutation_Added,
474 : eAnimationMutation_Changed,
475 : eAnimationMutation_Removed
476 : };
477 :
478 : void RecordAnimationMutation(mozilla::dom::Animation* aAnimation,
479 : AnimationMutation aMutationType);
480 : };
481 :
482 : #define NS_DOM_MUTATION_OBSERVER_IID \
483 : { 0x0c3b91f8, 0xcc3b, 0x4b08, \
484 : { 0x9e, 0xab, 0x07, 0x47, 0xa9, 0xe4, 0x65, 0xb4 } }
485 :
486 : class nsDOMMutationObserver final : public nsISupports,
487 : public nsWrapperCache
488 : {
489 : public:
490 1 : nsDOMMutationObserver(already_AddRefed<nsPIDOMWindowInner>&& aOwner,
491 : mozilla::dom::MutationCallback& aCb,
492 : bool aChrome)
493 1 : : mOwner(aOwner), mLastPendingMutation(nullptr), mPendingMutationCount(0),
494 : mCallback(&aCb), mWaitingForRun(false), mIsChrome(aChrome),
495 1 : mMergeAttributeRecords(false), mId(++sCount)
496 : {
497 1 : }
498 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
499 20 : NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMMutationObserver)
500 : NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_MUTATION_OBSERVER_IID)
501 :
502 : static already_AddRefed<nsDOMMutationObserver>
503 : Constructor(const mozilla::dom::GlobalObject& aGlobal,
504 : mozilla::dom::MutationCallback& aCb,
505 : mozilla::ErrorResult& aRv);
506 :
507 1 : virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
508 : {
509 1 : return mozilla::dom::MutationObserverBinding::Wrap(aCx, this, aGivenProto);
510 : }
511 :
512 1 : nsISupports* GetParentObject() const
513 : {
514 1 : return mOwner;
515 : }
516 :
517 0 : bool IsChrome()
518 : {
519 0 : return mIsChrome;
520 : }
521 :
522 : void Observe(nsINode& aTarget,
523 : const mozilla::dom::MutationObserverInit& aOptions,
524 : mozilla::ErrorResult& aRv);
525 :
526 : void Disconnect();
527 :
528 : void TakeRecords(nsTArray<RefPtr<nsDOMMutationRecord> >& aRetVal);
529 :
530 : void HandleMutation();
531 :
532 : void GetObservingInfo(nsTArray<Nullable<MutationObservingInfo>>& aResult,
533 : mozilla::ErrorResult& aRv);
534 :
535 0 : mozilla::dom::MutationCallback* MutationCallback() { return mCallback; }
536 :
537 0 : bool MergeAttributeRecords()
538 : {
539 0 : return mMergeAttributeRecords;
540 : }
541 :
542 0 : void SetMergeAttributeRecords(bool aVal)
543 : {
544 0 : mMergeAttributeRecords = aVal;
545 0 : }
546 :
547 : // If both records are for 'attributes' type and for the same target and
548 : // attribute name and namespace are the same, we can skip the newer record.
549 : // aOldRecord->mPrevValue holds the original value, if observed.
550 : bool MergeableAttributeRecord(nsDOMMutationRecord* aOldRecord,
551 : nsDOMMutationRecord* aRecord);
552 :
553 0 : void AppendMutationRecord(already_AddRefed<nsDOMMutationRecord> aRecord)
554 : {
555 0 : RefPtr<nsDOMMutationRecord> record = aRecord;
556 0 : MOZ_ASSERT(record);
557 0 : if (!mLastPendingMutation) {
558 0 : MOZ_ASSERT(!mFirstPendingMutation);
559 0 : mFirstPendingMutation = record.forget();
560 0 : mLastPendingMutation = mFirstPendingMutation;
561 : } else {
562 0 : MOZ_ASSERT(mFirstPendingMutation);
563 0 : mLastPendingMutation->mNext = record.forget();
564 0 : mLastPendingMutation = mLastPendingMutation->mNext;
565 : }
566 0 : ++mPendingMutationCount;
567 0 : }
568 :
569 0 : void ClearPendingRecords()
570 : {
571 0 : mFirstPendingMutation = nullptr;
572 0 : mLastPendingMutation = nullptr;
573 0 : mPendingMutationCount = 0;
574 0 : }
575 :
576 : // static methods
577 1733 : static void HandleMutations()
578 : {
579 1733 : if (sScheduledMutationObservers) {
580 0 : HandleMutationsInternal();
581 : }
582 1733 : }
583 :
584 : static void EnterMutationHandling();
585 : static void LeaveMutationHandling();
586 :
587 : static void Shutdown();
588 : protected:
589 : virtual ~nsDOMMutationObserver();
590 :
591 : friend class nsMutationReceiver;
592 : friend class nsAnimationReceiver;
593 : friend class nsAutoMutationBatch;
594 : friend class nsAutoAnimationMutationBatch;
595 : nsMutationReceiver* GetReceiverFor(nsINode* aNode,
596 : bool aMayCreate,
597 : bool aWantsAnimations);
598 : void RemoveReceiver(nsMutationReceiver* aReceiver);
599 :
600 : already_AddRefed<nsIVariant> TakeRecords();
601 :
602 : void GetAllSubtreeObserversFor(nsINode* aNode,
603 : nsTArray<nsMutationReceiver*>& aObservers);
604 : void ScheduleForRun();
605 : void RescheduleForRun();
606 :
607 : nsDOMMutationRecord* CurrentRecord(nsIAtom* aType);
608 : bool HasCurrentRecord(const nsAString& aType);
609 :
610 0 : bool Suppressed()
611 : {
612 0 : if (mOwner) {
613 0 : nsCOMPtr<nsIDocument> d = mOwner->GetExtantDoc();
614 0 : return d && d->IsInSyncOperation();
615 : }
616 0 : return false;
617 : }
618 :
619 : static void HandleMutationsInternal();
620 :
621 : static void AddCurrentlyHandlingObserver(nsDOMMutationObserver* aObserver,
622 : uint32_t aMutationLevel);
623 :
624 : nsCOMPtr<nsPIDOMWindowInner> mOwner;
625 :
626 : nsCOMArray<nsMutationReceiver> mReceivers;
627 : nsClassHashtable<nsISupportsHashKey,
628 : nsCOMArray<nsMutationReceiver> > mTransientReceivers;
629 : // MutationRecords which are being constructed.
630 : AutoTArray<nsDOMMutationRecord*, 4> mCurrentMutations;
631 : // MutationRecords which will be handed to the callback at the end of
632 : // the microtask.
633 : RefPtr<nsDOMMutationRecord> mFirstPendingMutation;
634 : nsDOMMutationRecord* mLastPendingMutation;
635 : uint32_t mPendingMutationCount;
636 :
637 : RefPtr<mozilla::dom::MutationCallback> mCallback;
638 :
639 : bool mWaitingForRun;
640 : bool mIsChrome;
641 : bool mMergeAttributeRecords;
642 :
643 : uint64_t mId;
644 :
645 : static uint64_t sCount;
646 : static AutoTArray<RefPtr<nsDOMMutationObserver>, 4>* sScheduledMutationObservers;
647 : static nsDOMMutationObserver* sCurrentObserver;
648 :
649 : static uint32_t sMutationLevel;
650 : static AutoTArray<AutoTArray<RefPtr<nsDOMMutationObserver>, 4>, 4>*
651 : sCurrentlyHandlingObservers;
652 : };
653 :
654 : NS_DEFINE_STATIC_IID_ACCESSOR(nsDOMMutationObserver, NS_DOM_MUTATION_OBSERVER_IID)
655 :
656 : class nsAutoMutationBatch
657 : {
658 : public:
659 96 : nsAutoMutationBatch()
660 96 : : mPreviousBatch(nullptr), mBatchTarget(nullptr), mRemovalDone(false),
661 96 : mFromFirstToLast(false), mAllowNestedBatches(false)
662 : {
663 96 : }
664 :
665 7 : nsAutoMutationBatch(nsINode* aTarget, bool aFromFirstToLast,
666 : bool aAllowNestedBatches)
667 7 : : mPreviousBatch(nullptr), mBatchTarget(nullptr), mRemovalDone(false),
668 7 : mFromFirstToLast(false), mAllowNestedBatches(false)
669 : {
670 7 : Init(aTarget, aFromFirstToLast, aAllowNestedBatches);
671 7 : }
672 :
673 13 : void Init(nsINode* aTarget, bool aFromFirstToLast, bool aAllowNestedBatches)
674 : {
675 13 : if (aTarget && aTarget->OwnerDoc()->MayHaveDOMMutationObservers()) {
676 10 : if (sCurrentBatch && !sCurrentBatch->mAllowNestedBatches) {
677 0 : return;
678 : }
679 10 : mBatchTarget = aTarget;
680 10 : mFromFirstToLast = aFromFirstToLast;
681 10 : mAllowNestedBatches = aAllowNestedBatches;
682 10 : mPreviousBatch = sCurrentBatch;
683 10 : sCurrentBatch = this;
684 10 : nsDOMMutationObserver::EnterMutationHandling();
685 : }
686 : }
687 :
688 8 : void RemovalDone() { mRemovalDone = true; }
689 0 : static bool IsRemovalDone() { return sCurrentBatch->mRemovalDone; }
690 :
691 6 : void SetPrevSibling(nsINode* aNode) { mPrevSibling = aNode; }
692 6 : void SetNextSibling(nsINode* aNode) { mNextSibling = aNode; }
693 :
694 : void Done();
695 :
696 103 : ~nsAutoMutationBatch() { NodesAdded(); }
697 :
698 0 : static bool IsBatching()
699 : {
700 0 : return !!sCurrentBatch;
701 : }
702 :
703 97 : static nsAutoMutationBatch* GetCurrentBatch()
704 : {
705 97 : return sCurrentBatch;
706 : }
707 :
708 0 : static void UpdateObserver(nsDOMMutationObserver* aObserver,
709 : bool aWantsChildList)
710 : {
711 0 : uint32_t l = sCurrentBatch->mObservers.Length();
712 0 : for (uint32_t i = 0; i < l; ++i) {
713 0 : if (sCurrentBatch->mObservers[i].mObserver == aObserver) {
714 0 : if (aWantsChildList) {
715 0 : sCurrentBatch->mObservers[i].mWantsChildList = aWantsChildList;
716 : }
717 0 : return;
718 : }
719 : }
720 0 : BatchObserver* bo = sCurrentBatch->mObservers.AppendElement();
721 0 : bo->mObserver = aObserver;
722 0 : bo->mWantsChildList = aWantsChildList;
723 : }
724 :
725 :
726 0 : static nsINode* GetBatchTarget() { return sCurrentBatch->mBatchTarget; }
727 :
728 : // Mutation receivers notify the batch about removed child nodes.
729 0 : static void NodeRemoved(nsIContent* aChild)
730 : {
731 0 : if (IsBatching() && !sCurrentBatch->mRemovalDone) {
732 0 : uint32_t len = sCurrentBatch->mRemovedNodes.Length();
733 0 : if (!len ||
734 0 : sCurrentBatch->mRemovedNodes[len - 1] != aChild) {
735 0 : sCurrentBatch->mRemovedNodes.AppendElement(aChild);
736 : }
737 : }
738 0 : }
739 :
740 : // Called after new child nodes have been added to the batch target.
741 109 : void NodesAdded()
742 : {
743 109 : if (sCurrentBatch != this) {
744 99 : return;
745 : }
746 :
747 : nsIContent* c =
748 16 : mPrevSibling ? mPrevSibling->GetNextSibling() :
749 26 : mBatchTarget->GetFirstChild();
750 82 : for (; c != mNextSibling; c = c->GetNextSibling()) {
751 36 : mAddedNodes.AppendElement(c);
752 : }
753 10 : Done();
754 : }
755 :
756 : private:
757 : struct BatchObserver
758 : {
759 : nsDOMMutationObserver* mObserver;
760 : bool mWantsChildList;
761 : };
762 :
763 : static nsAutoMutationBatch* sCurrentBatch;
764 : nsAutoMutationBatch* mPreviousBatch;
765 : AutoTArray<BatchObserver, 2> mObservers;
766 : nsTArray<nsCOMPtr<nsIContent> > mRemovedNodes;
767 : nsTArray<nsCOMPtr<nsIContent> > mAddedNodes;
768 : nsINode* mBatchTarget;
769 : bool mRemovalDone;
770 : bool mFromFirstToLast;
771 : bool mAllowNestedBatches;
772 : nsCOMPtr<nsINode> mPrevSibling;
773 : nsCOMPtr<nsINode> mNextSibling;
774 : };
775 :
776 : class nsAutoAnimationMutationBatch
777 : {
778 : struct Entry;
779 :
780 : public:
781 86 : explicit nsAutoAnimationMutationBatch(nsIDocument* aDocument)
782 86 : {
783 86 : Init(aDocument);
784 86 : }
785 :
786 86 : void Init(nsIDocument* aDocument)
787 : {
788 172 : if (!aDocument ||
789 159 : !aDocument->MayHaveDOMMutationObservers() ||
790 73 : sCurrentBatch) {
791 13 : return;
792 : }
793 :
794 73 : sCurrentBatch = this;
795 73 : nsDOMMutationObserver::EnterMutationHandling();
796 : }
797 :
798 86 : ~nsAutoAnimationMutationBatch()
799 86 : {
800 86 : Done();
801 86 : }
802 :
803 : void Done();
804 :
805 0 : static bool IsBatching()
806 : {
807 0 : return !!sCurrentBatch;
808 : }
809 :
810 : static nsAutoAnimationMutationBatch* GetCurrentBatch()
811 : {
812 : return sCurrentBatch;
813 : }
814 :
815 0 : static void AddObserver(nsDOMMutationObserver* aObserver)
816 : {
817 0 : if (sCurrentBatch->mObservers.Contains(aObserver)) {
818 0 : return;
819 : }
820 0 : sCurrentBatch->mObservers.AppendElement(aObserver);
821 : }
822 :
823 0 : static void AnimationAdded(mozilla::dom::Animation* aAnimation,
824 : nsINode* aTarget)
825 : {
826 0 : if (!IsBatching()) {
827 0 : return;
828 : }
829 :
830 0 : Entry* entry = sCurrentBatch->FindEntry(aAnimation, aTarget);
831 0 : if (entry) {
832 0 : switch (entry->mState) {
833 : case eState_RemainedAbsent:
834 0 : entry->mState = eState_Added;
835 0 : break;
836 : case eState_Removed:
837 0 : entry->mState = eState_RemainedPresent;
838 0 : break;
839 : default:
840 0 : NS_NOTREACHED("shouldn't have observed an animation being added "
841 : "twice");
842 : }
843 : } else {
844 0 : entry = sCurrentBatch->AddEntry(aAnimation, aTarget);
845 0 : entry->mState = eState_Added;
846 0 : entry->mChanged = false;
847 : }
848 : }
849 :
850 0 : static void AnimationChanged(mozilla::dom::Animation* aAnimation,
851 : nsINode* aTarget)
852 : {
853 0 : Entry* entry = sCurrentBatch->FindEntry(aAnimation, aTarget);
854 0 : if (entry) {
855 0 : NS_ASSERTION(entry->mState == eState_RemainedPresent ||
856 : entry->mState == eState_Added,
857 : "shouldn't have observed an animation being changed after "
858 : "being removed");
859 0 : entry->mChanged = true;
860 : } else {
861 0 : entry = sCurrentBatch->AddEntry(aAnimation, aTarget);
862 0 : entry->mState = eState_RemainedPresent;
863 0 : entry->mChanged = true;
864 : }
865 0 : }
866 :
867 0 : static void AnimationRemoved(mozilla::dom::Animation* aAnimation,
868 : nsINode* aTarget)
869 : {
870 0 : Entry* entry = sCurrentBatch->FindEntry(aAnimation, aTarget);
871 0 : if (entry) {
872 0 : switch (entry->mState) {
873 : case eState_RemainedPresent:
874 0 : entry->mState = eState_Removed;
875 0 : break;
876 : case eState_Added:
877 0 : entry->mState = eState_RemainedAbsent;
878 0 : break;
879 : default:
880 0 : NS_NOTREACHED("shouldn't have observed an animation being removed "
881 : "twice");
882 : }
883 : } else {
884 0 : entry = sCurrentBatch->AddEntry(aAnimation, aTarget);
885 0 : entry->mState = eState_Removed;
886 0 : entry->mChanged = false;
887 : }
888 0 : }
889 :
890 : private:
891 0 : Entry* FindEntry(mozilla::dom::Animation* aAnimation, nsINode* aTarget)
892 : {
893 0 : EntryArray* entries = mEntryTable.Get(aTarget);
894 0 : if (!entries) {
895 0 : return nullptr;
896 : }
897 :
898 0 : for (Entry& e : *entries) {
899 0 : if (e.mAnimation == aAnimation) {
900 0 : return &e;
901 : }
902 : }
903 0 : return nullptr;
904 : }
905 :
906 0 : Entry* AddEntry(mozilla::dom::Animation* aAnimation, nsINode* aTarget)
907 : {
908 0 : EntryArray* entries = sCurrentBatch->mEntryTable.LookupOrAdd(aTarget);
909 0 : if (entries->IsEmpty()) {
910 0 : sCurrentBatch->mBatchTargets.AppendElement(aTarget);
911 : }
912 0 : Entry* entry = entries->AppendElement();
913 0 : entry->mAnimation = aAnimation;
914 0 : return entry;
915 : }
916 :
917 : enum State {
918 : eState_RemainedPresent,
919 : eState_RemainedAbsent,
920 : eState_Added,
921 : eState_Removed
922 : };
923 :
924 0 : struct Entry
925 : {
926 : RefPtr<mozilla::dom::Animation> mAnimation;
927 : State mState;
928 : bool mChanged;
929 : };
930 :
931 : static nsAutoAnimationMutationBatch* sCurrentBatch;
932 : AutoTArray<nsDOMMutationObserver*, 2> mObservers;
933 : typedef nsTArray<Entry> EntryArray;
934 : nsClassHashtable<nsPtrHashKey<nsINode>, EntryArray> mEntryTable;
935 : // List of nodes referred to by mEntryTable so we can sort them
936 : // For a specific pseudo element, we use its parent element as the
937 : // batch target, so they will be put in the same EntryArray.
938 : nsTArray<nsINode*> mBatchTargets;
939 : };
940 :
941 : inline
942 : nsDOMMutationObserver*
943 0 : nsMutationReceiverBase::Observer()
944 : {
945 0 : return mParent ?
946 0 : mParent->Observer() : static_cast<nsDOMMutationObserver*>(mObserver);
947 : }
948 :
949 : #endif
|