content/base/src/nsDOMMutationObserver.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

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

mercurial