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