Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
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 #ifndef nsDOMMutationObserver_h
8 #define nsDOMMutationObserver_h
10 #include "mozilla/Attributes.h"
11 #include "nsCycleCollectionParticipant.h"
12 #include "nsPIDOMWindow.h"
13 #include "nsIScriptContext.h"
14 #include "nsStubMutationObserver.h"
15 #include "nsCOMArray.h"
16 #include "nsTArray.h"
17 #include "nsAutoPtr.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"
28 class nsDOMMutationObserver;
29 using mozilla::dom::MutationObservingInfo;
31 class nsDOMMutationRecord : public nsISupports,
32 public nsWrapperCache
33 {
34 public:
35 nsDOMMutationRecord(nsIAtom* aType, nsISupports* aOwner)
36 : mType(aType), mAttrNamespace(NullString()), mPrevValue(NullString()), mOwner(aOwner)
37 {
38 SetIsDOMBinding();
39 }
40 virtual ~nsDOMMutationRecord() {}
42 nsISupports* GetParentObject() const
43 {
44 return mOwner;
45 }
47 virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE
48 {
49 return mozilla::dom::MutationRecordBinding::Wrap(aCx, this);
50 }
52 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
53 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMMutationRecord)
55 void GetType(mozilla::dom::DOMString& aRetVal) const
56 {
57 aRetVal.SetOwnedAtom(mType, mozilla::dom::DOMString::eNullNotExpected);
58 }
60 nsINode* GetTarget() const
61 {
62 return mTarget;
63 }
65 nsINodeList* AddedNodes();
67 nsINodeList* RemovedNodes();
69 nsINode* GetPreviousSibling() const
70 {
71 return mPreviousSibling;
72 }
74 nsINode* GetNextSibling() const
75 {
76 return mNextSibling;
77 }
79 void GetAttributeName(mozilla::dom::DOMString& aRetVal) const
80 {
81 aRetVal.SetOwnedAtom(mAttrName, mozilla::dom::DOMString::eTreatNullAsNull);
82 }
84 void GetAttributeNamespace(mozilla::dom::DOMString& aRetVal) const
85 {
86 aRetVal.SetOwnedString(mAttrNamespace);
87 }
89 void GetOldValue(mozilla::dom::DOMString& aRetVal) const
90 {
91 aRetVal.SetOwnedString(mPrevValue);
92 }
94 nsCOMPtr<nsINode> mTarget;
95 nsCOMPtr<nsIAtom> mType;
96 nsCOMPtr<nsIAtom> mAttrName;
97 nsString mAttrNamespace;
98 nsString mPrevValue;
99 nsRefPtr<nsSimpleContentList> mAddedNodes;
100 nsRefPtr<nsSimpleContentList> mRemovedNodes;
101 nsCOMPtr<nsINode> mPreviousSibling;
102 nsCOMPtr<nsINode> mNextSibling;
104 nsRefPtr<nsDOMMutationRecord> mNext;
105 nsCOMPtr<nsISupports> mOwner;
106 };
108 // Base class just prevents direct access to
109 // members to make sure we go through getters/setters.
110 class nsMutationReceiverBase : public nsStubMutationObserver
111 {
112 public:
113 virtual ~nsMutationReceiverBase() { }
115 nsDOMMutationObserver* Observer();
116 nsINode* Target() { return mParent ? mParent->Target() : mTarget; }
117 nsINode* RegisterTarget() { return mRegisterTarget; }
119 bool Subtree() { return mParent ? mParent->Subtree() : mSubtree; }
120 void SetSubtree(bool aSubtree)
121 {
122 NS_ASSERTION(!mParent, "Shouldn't have parent");
123 mSubtree = aSubtree;
124 }
126 bool ChildList() { return mParent ? mParent->ChildList() : mChildList; }
127 void SetChildList(bool aChildList)
128 {
129 NS_ASSERTION(!mParent, "Shouldn't have parent");
130 mChildList = aChildList;
131 }
133 bool CharacterData()
134 {
135 return mParent ? mParent->CharacterData() : mCharacterData;
136 }
137 void SetCharacterData(bool aCharacterData)
138 {
139 NS_ASSERTION(!mParent, "Shouldn't have parent");
140 mCharacterData = aCharacterData;
141 }
143 bool CharacterDataOldValue()
144 {
145 return mParent ? mParent->CharacterDataOldValue() : mCharacterDataOldValue;
146 }
147 void SetCharacterDataOldValue(bool aOldValue)
148 {
149 NS_ASSERTION(!mParent, "Shouldn't have parent");
150 mCharacterDataOldValue = aOldValue;
151 }
153 bool Attributes() { return mParent ? mParent->Attributes() : mAttributes; }
154 void SetAttributes(bool aAttributes)
155 {
156 NS_ASSERTION(!mParent, "Shouldn't have parent");
157 mAttributes = aAttributes;
158 }
160 bool AllAttributes()
161 {
162 return mParent ? mParent->AllAttributes()
163 : mAllAttributes;
164 }
165 void SetAllAttributes(bool aAll)
166 {
167 NS_ASSERTION(!mParent, "Shouldn't have parent");
168 mAllAttributes = aAll;
169 }
171 bool AttributeOldValue() {
172 return mParent ? mParent->AttributeOldValue()
173 : mAttributeOldValue;
174 }
175 void SetAttributeOldValue(bool aOldValue)
176 {
177 NS_ASSERTION(!mParent, "Shouldn't have parent");
178 mAttributeOldValue = aOldValue;
179 }
181 nsCOMArray<nsIAtom>& AttributeFilter() { return mAttributeFilter; }
182 void SetAttributeFilter(nsCOMArray<nsIAtom>& aFilter)
183 {
184 NS_ASSERTION(!mParent, "Shouldn't have parent");
185 mAttributeFilter.Clear();
186 mAttributeFilter.AppendObjects(aFilter);
187 }
189 void AddClone(nsMutationReceiverBase* aClone)
190 {
191 mTransientReceivers.AppendObject(aClone);
192 }
194 void RemoveClone(nsMutationReceiverBase* aClone)
195 {
196 mTransientReceivers.RemoveObject(aClone);
197 }
199 protected:
200 nsMutationReceiverBase(nsINode* aTarget, nsDOMMutationObserver* aObserver)
201 : mTarget(aTarget), mObserver(aObserver), mRegisterTarget(aTarget)
202 {
203 mRegisterTarget->AddMutationObserver(this);
204 mRegisterTarget->SetMayHaveDOMMutationObserver();
205 mRegisterTarget->OwnerDoc()->SetMayHaveDOMMutationObservers();
206 }
208 nsMutationReceiverBase(nsINode* aRegisterTarget,
209 nsMutationReceiverBase* aParent)
210 : mTarget(nullptr), mObserver(nullptr), mParent(aParent),
211 mRegisterTarget(aRegisterTarget), mKungFuDeathGrip(aParent->Target())
212 {
213 NS_ASSERTION(mParent->Subtree(), "Should clone a non-subtree observer!");
214 mRegisterTarget->AddMutationObserver(this);
215 mRegisterTarget->SetMayHaveDOMMutationObserver();
216 mRegisterTarget->OwnerDoc()->SetMayHaveDOMMutationObservers();
217 }
219 bool ObservesAttr(mozilla::dom::Element* aElement,
220 int32_t aNameSpaceID,
221 nsIAtom* aAttr)
222 {
223 if (mParent) {
224 return mParent->ObservesAttr(aElement, aNameSpaceID, aAttr);
225 }
226 if (!Attributes() || (!Subtree() && aElement != Target())) {
227 return false;
228 }
229 if (AllAttributes()) {
230 return true;
231 }
233 if (aNameSpaceID != kNameSpaceID_None) {
234 return false;
235 }
237 nsCOMArray<nsIAtom>& filters = AttributeFilter();
238 for (int32_t i = 0; i < filters.Count(); ++i) {
239 if (filters[i] == aAttr) {
240 return true;
241 }
242 }
243 return false;
244 }
246 // The target for the MutationObserver.observe() method.
247 nsINode* mTarget;
248 nsDOMMutationObserver* mObserver;
249 nsRefPtr<nsMutationReceiverBase> mParent; // Cleared after microtask.
250 // The node to which Gecko-internal nsIMutationObserver was registered to.
251 // This is different than mTarget when dealing with transient observers.
252 nsINode* mRegisterTarget;
253 nsCOMArray<nsMutationReceiverBase> mTransientReceivers;
254 // While we have transient receivers, keep the original mutation receiver
255 // alive so it doesn't go away and disconnect all its transient receivers.
256 nsCOMPtr<nsINode> mKungFuDeathGrip;
258 private:
259 bool mSubtree;
260 bool mChildList;
261 bool mCharacterData;
262 bool mCharacterDataOldValue;
263 bool mAttributes;
264 bool mAllAttributes;
265 bool mAttributeOldValue;
266 nsCOMArray<nsIAtom> mAttributeFilter;
267 };
270 class nsMutationReceiver : public nsMutationReceiverBase
271 {
272 public:
273 nsMutationReceiver(nsINode* aTarget, nsDOMMutationObserver* aObserver);
275 nsMutationReceiver(nsINode* aRegisterTarget, nsMutationReceiverBase* aParent)
276 : nsMutationReceiverBase(aRegisterTarget, aParent)
277 {
278 NS_ASSERTION(!static_cast<nsMutationReceiver*>(aParent)->GetParent(),
279 "Shouldn't create deep observer hierarchies!");
280 aParent->AddClone(this);
281 }
283 virtual ~nsMutationReceiver() { Disconnect(false); }
285 nsMutationReceiver* GetParent()
286 {
287 return static_cast<nsMutationReceiver*>(mParent.get());
288 }
290 void RemoveClones()
291 {
292 for (int32_t i = 0; i < mTransientReceivers.Count(); ++i) {
293 nsMutationReceiver* r =
294 static_cast<nsMutationReceiver*>(mTransientReceivers[i]);
295 r->DisconnectTransientReceiver();
296 }
297 mTransientReceivers.Clear();
298 }
300 void DisconnectTransientReceiver()
301 {
302 if (mRegisterTarget) {
303 mRegisterTarget->RemoveMutationObserver(this);
304 mRegisterTarget = nullptr;
305 }
307 mParent = nullptr;
308 NS_ASSERTION(!mTarget, "Should not have mTarget");
309 NS_ASSERTION(!mObserver, "Should not have mObserver");
310 }
312 void Disconnect(bool aRemoveFromObserver);
314 NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
315 NS_DECL_ISUPPORTS
317 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
318 NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATAWILLCHANGE
319 NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
320 NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
321 NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
322 NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
324 virtual void AttributeSetToCurrentValue(nsIDocument* aDocument,
325 mozilla::dom::Element* aElement,
326 int32_t aNameSpaceID,
327 nsIAtom* aAttribute) MOZ_OVERRIDE
328 {
329 // We can reuse AttributeWillChange implementation.
330 AttributeWillChange(aDocument, aElement, aNameSpaceID, aAttribute,
331 nsIDOMMutationEvent::MODIFICATION);
332 }
333 };
335 #define NS_DOM_MUTATION_OBSERVER_IID \
336 { 0x0c3b91f8, 0xcc3b, 0x4b08, \
337 { 0x9e, 0xab, 0x07, 0x47, 0xa9, 0xe4, 0x65, 0xb4 } }
339 class nsDOMMutationObserver : public nsISupports,
340 public nsWrapperCache
341 {
342 public:
343 nsDOMMutationObserver(already_AddRefed<nsPIDOMWindow>&& aOwner,
344 mozilla::dom::MutationCallback& aCb)
345 : mOwner(aOwner), mLastPendingMutation(nullptr), mPendingMutationCount(0),
346 mCallback(&aCb), mWaitingForRun(false), mId(++sCount)
347 {
348 SetIsDOMBinding();
349 }
350 virtual ~nsDOMMutationObserver();
351 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
352 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMMutationObserver)
353 NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_MUTATION_OBSERVER_IID)
355 static already_AddRefed<nsDOMMutationObserver>
356 Constructor(const mozilla::dom::GlobalObject& aGlobal,
357 mozilla::dom::MutationCallback& aCb,
358 mozilla::ErrorResult& aRv);
360 virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE
361 {
362 return mozilla::dom::MutationObserverBinding::Wrap(aCx, this);
363 }
365 nsISupports* GetParentObject() const
366 {
367 return mOwner;
368 }
370 void Observe(nsINode& aTarget,
371 const mozilla::dom::MutationObserverInit& aOptions,
372 mozilla::ErrorResult& aRv);
374 void Disconnect();
376 void TakeRecords(nsTArray<nsRefPtr<nsDOMMutationRecord> >& aRetVal);
378 void HandleMutation();
380 void GetObservingInfo(nsTArray<Nullable<MutationObservingInfo> >& aResult);
382 mozilla::dom::MutationCallback* MutationCallback() { return mCallback; }
384 void AppendMutationRecord(already_AddRefed<nsDOMMutationRecord> aRecord)
385 {
386 nsRefPtr<nsDOMMutationRecord> record = aRecord;
387 MOZ_ASSERT(record);
388 if (!mLastPendingMutation) {
389 MOZ_ASSERT(!mFirstPendingMutation);
390 mFirstPendingMutation = record.forget();
391 mLastPendingMutation = mFirstPendingMutation;
392 } else {
393 MOZ_ASSERT(mFirstPendingMutation);
394 mLastPendingMutation->mNext = record.forget();
395 mLastPendingMutation = mLastPendingMutation->mNext;
396 }
397 ++mPendingMutationCount;
398 }
400 void ClearPendingRecords()
401 {
402 mFirstPendingMutation = nullptr;
403 mLastPendingMutation = nullptr;
404 mPendingMutationCount = 0;
405 }
407 // static methods
408 static void HandleMutations()
409 {
410 if (sScheduledMutationObservers) {
411 HandleMutationsInternal();
412 }
413 }
415 static void EnterMutationHandling();
416 static void LeaveMutationHandling();
418 static void Shutdown();
419 protected:
420 friend class nsMutationReceiver;
421 friend class nsAutoMutationBatch;
422 nsMutationReceiver* GetReceiverFor(nsINode* aNode, bool aMayCreate);
423 void RemoveReceiver(nsMutationReceiver* aReceiver);
425 already_AddRefed<nsIVariant> TakeRecords();
427 void GetAllSubtreeObserversFor(nsINode* aNode,
428 nsTArray<nsMutationReceiver*>& aObservers);
429 void ScheduleForRun();
430 void RescheduleForRun();
432 nsDOMMutationRecord* CurrentRecord(nsIAtom* aType);
433 bool HasCurrentRecord(const nsAString& aType);
435 bool Suppressed()
436 {
437 if (mOwner) {
438 nsCOMPtr<nsIDocument> d = mOwner->GetExtantDoc();
439 return d && d->IsInSyncOperation();
440 }
441 return false;
442 }
444 static void HandleMutationsInternal();
446 static void AddCurrentlyHandlingObserver(nsDOMMutationObserver* aObserver);
448 nsCOMPtr<nsPIDOMWindow> mOwner;
450 nsCOMArray<nsMutationReceiver> mReceivers;
451 nsClassHashtable<nsISupportsHashKey,
452 nsCOMArray<nsMutationReceiver> > mTransientReceivers;
453 // MutationRecords which are being constructed.
454 nsAutoTArray<nsDOMMutationRecord*, 4> mCurrentMutations;
455 // MutationRecords which will be handed to the callback at the end of
456 // the microtask.
457 nsRefPtr<nsDOMMutationRecord> mFirstPendingMutation;
458 nsDOMMutationRecord* mLastPendingMutation;
459 uint32_t mPendingMutationCount;
461 nsRefPtr<mozilla::dom::MutationCallback> mCallback;
463 bool mWaitingForRun;
465 uint64_t mId;
467 static uint64_t sCount;
468 static nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>* sScheduledMutationObservers;
469 static nsDOMMutationObserver* sCurrentObserver;
471 static uint32_t sMutationLevel;
472 static nsAutoTArray<nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>, 4>*
473 sCurrentlyHandlingObservers;
474 };
476 NS_DEFINE_STATIC_IID_ACCESSOR(nsDOMMutationObserver, NS_DOM_MUTATION_OBSERVER_IID)
478 class nsAutoMutationBatch
479 {
480 public:
481 nsAutoMutationBatch()
482 : mPreviousBatch(nullptr), mBatchTarget(nullptr), mRemovalDone(false),
483 mFromFirstToLast(false), mAllowNestedBatches(false)
484 {
485 }
487 nsAutoMutationBatch(nsINode* aTarget, bool aFromFirstToLast,
488 bool aAllowNestedBatches)
489 : mPreviousBatch(nullptr), mBatchTarget(nullptr), mRemovalDone(false),
490 mFromFirstToLast(false), mAllowNestedBatches(false)
491 {
492 Init(aTarget, aFromFirstToLast, aAllowNestedBatches);
493 }
495 void Init(nsINode* aTarget, bool aFromFirstToLast, bool aAllowNestedBatches)
496 {
497 if (aTarget && aTarget->OwnerDoc()->MayHaveDOMMutationObservers()) {
498 if (sCurrentBatch && !sCurrentBatch->mAllowNestedBatches) {
499 return;
500 }
501 mBatchTarget = aTarget;
502 mFromFirstToLast = aFromFirstToLast;
503 mAllowNestedBatches = aAllowNestedBatches;
504 mPreviousBatch = sCurrentBatch;
505 sCurrentBatch = this;
506 nsDOMMutationObserver::EnterMutationHandling();
507 }
508 }
510 void RemovalDone() { mRemovalDone = true; }
511 static bool IsRemovalDone() { return sCurrentBatch->mRemovalDone; }
513 void SetPrevSibling(nsINode* aNode) { mPrevSibling = aNode; }
514 void SetNextSibling(nsINode* aNode) { mNextSibling = aNode; }
516 void Done();
518 ~nsAutoMutationBatch() { NodesAdded(); }
520 static bool IsBatching()
521 {
522 return !!sCurrentBatch;
523 }
525 static nsAutoMutationBatch* GetCurrentBatch()
526 {
527 return sCurrentBatch;
528 }
530 static void UpdateObserver(nsDOMMutationObserver* aObserver,
531 bool aWantsChildList)
532 {
533 uint32_t l = sCurrentBatch->mObservers.Length();
534 for (uint32_t i = 0; i < l; ++i) {
535 if (sCurrentBatch->mObservers[i].mObserver == aObserver) {
536 if (aWantsChildList) {
537 sCurrentBatch->mObservers[i].mWantsChildList = aWantsChildList;
538 }
539 return;
540 }
541 }
542 BatchObserver* bo = sCurrentBatch->mObservers.AppendElement();
543 bo->mObserver = aObserver;
544 bo->mWantsChildList = aWantsChildList;
545 }
548 static nsINode* GetBatchTarget() { return sCurrentBatch->mBatchTarget; }
550 // Mutation receivers notify the batch about removed child nodes.
551 static void NodeRemoved(nsIContent* aChild)
552 {
553 if (IsBatching() && !sCurrentBatch->mRemovalDone) {
554 uint32_t len = sCurrentBatch->mRemovedNodes.Length();
555 if (!len ||
556 sCurrentBatch->mRemovedNodes[len - 1] != aChild) {
557 sCurrentBatch->mRemovedNodes.AppendElement(aChild);
558 }
559 }
560 }
562 // Called after new child nodes have been added to the batch target.
563 void NodesAdded()
564 {
565 if (sCurrentBatch != this) {
566 return;
567 }
569 nsIContent* c =
570 mPrevSibling ? mPrevSibling->GetNextSibling() :
571 mBatchTarget->GetFirstChild();
572 for (; c != mNextSibling; c = c->GetNextSibling()) {
573 mAddedNodes.AppendElement(c);
574 }
575 Done();
576 }
578 private:
579 struct BatchObserver
580 {
581 nsDOMMutationObserver* mObserver;
582 bool mWantsChildList;
583 };
585 static nsAutoMutationBatch* sCurrentBatch;
586 nsAutoMutationBatch* mPreviousBatch;
587 nsAutoTArray<BatchObserver, 2> mObservers;
588 nsTArray<nsCOMPtr<nsIContent> > mRemovedNodes;
589 nsTArray<nsCOMPtr<nsIContent> > mAddedNodes;
590 nsINode* mBatchTarget;
591 bool mRemovalDone;
592 bool mFromFirstToLast;
593 bool mAllowNestedBatches;
594 nsCOMPtr<nsINode> mPrevSibling;
595 nsCOMPtr<nsINode> mNextSibling;
596 };
598 inline
599 nsDOMMutationObserver*
600 nsMutationReceiverBase::Observer()
601 {
602 return mParent ?
603 mParent->Observer() : static_cast<nsDOMMutationObserver*>(mObserver);
604 }
606 #endif