content/base/src/nsDOMMutationObserver.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/base/src/nsDOMMutationObserver.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,848 @@
     1.4 +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- */
     1.5 +/* vim: set sw=4 ts=8 et tw=80 : */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "nsDOMMutationObserver.h"
    1.11 +
    1.12 +#include "mozilla/dom/OwningNonNull.h"
    1.13 +#include "nsError.h"
    1.14 +#include "nsIScriptGlobalObject.h"
    1.15 +#include "nsContentUtils.h"
    1.16 +#include "nsThreadUtils.h"
    1.17 +#include "nsIDOMMutationEvent.h"
    1.18 +#include "nsTextFragment.h"
    1.19 +#include "nsServiceManagerUtils.h"
    1.20 +
    1.21 +nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>*
    1.22 +  nsDOMMutationObserver::sScheduledMutationObservers = nullptr;
    1.23 +
    1.24 +nsDOMMutationObserver* nsDOMMutationObserver::sCurrentObserver = nullptr;
    1.25 +
    1.26 +uint32_t nsDOMMutationObserver::sMutationLevel = 0;
    1.27 +uint64_t nsDOMMutationObserver::sCount = 0;
    1.28 +
    1.29 +nsAutoTArray<nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>, 4>*
    1.30 +nsDOMMutationObserver::sCurrentlyHandlingObservers = nullptr;
    1.31 +
    1.32 +nsINodeList*
    1.33 +nsDOMMutationRecord::AddedNodes()
    1.34 +{
    1.35 +  if (!mAddedNodes) {
    1.36 +    mAddedNodes = new nsSimpleContentList(mTarget);
    1.37 +  }
    1.38 +  return mAddedNodes;
    1.39 +}
    1.40 +
    1.41 +nsINodeList*
    1.42 +nsDOMMutationRecord::RemovedNodes()
    1.43 +{
    1.44 +  if (!mRemovedNodes) {
    1.45 +    mRemovedNodes = new nsSimpleContentList(mTarget);
    1.46 +  }
    1.47 +  return mRemovedNodes;
    1.48 +}
    1.49 +
    1.50 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMMutationRecord)
    1.51 +  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    1.52 +  NS_INTERFACE_MAP_ENTRY(nsISupports)
    1.53 +NS_INTERFACE_MAP_END
    1.54 +
    1.55 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMMutationRecord)
    1.56 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMMutationRecord)
    1.57 +
    1.58 +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_7(nsDOMMutationRecord,
    1.59 +                                        mTarget,
    1.60 +                                        mPreviousSibling, mNextSibling,
    1.61 +                                        mAddedNodes, mRemovedNodes,
    1.62 +                                        mNext, mOwner)
    1.63 +
    1.64 +// Observer
    1.65 +
    1.66 +NS_IMPL_ADDREF(nsMutationReceiver)
    1.67 +NS_IMPL_RELEASE(nsMutationReceiver)
    1.68 +
    1.69 +NS_INTERFACE_MAP_BEGIN(nsMutationReceiver)
    1.70 +  NS_INTERFACE_MAP_ENTRY(nsISupports)
    1.71 +  NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
    1.72 +NS_INTERFACE_MAP_END
    1.73 +
    1.74 +nsMutationReceiver::nsMutationReceiver(nsINode* aTarget,
    1.75 +                                       nsDOMMutationObserver* aObserver)
    1.76 +: nsMutationReceiverBase(aTarget, aObserver)
    1.77 +{
    1.78 +  mTarget->BindObject(aObserver);
    1.79 +}
    1.80 +
    1.81 +void
    1.82 +nsMutationReceiver::Disconnect(bool aRemoveFromObserver)
    1.83 +{
    1.84 +  if (mRegisterTarget) {
    1.85 +    mRegisterTarget->RemoveMutationObserver(this);
    1.86 +    mRegisterTarget = nullptr;
    1.87 +  }
    1.88 +
    1.89 +  mParent = nullptr;
    1.90 +  nsINode* target = mTarget;
    1.91 +  mTarget = nullptr;
    1.92 +  nsDOMMutationObserver* observer = mObserver;
    1.93 +  mObserver = nullptr;
    1.94 +  RemoveClones();
    1.95 +
    1.96 +  if (target && observer) {
    1.97 +    if (aRemoveFromObserver) {
    1.98 +      static_cast<nsDOMMutationObserver*>(observer)->RemoveReceiver(this);
    1.99 +    }
   1.100 +    // UnbindObject may delete 'this'!
   1.101 +    target->UnbindObject(observer);
   1.102 +  }
   1.103 +}
   1.104 +
   1.105 +void
   1.106 +nsMutationReceiver::AttributeWillChange(nsIDocument* aDocument,
   1.107 +                                        mozilla::dom::Element* aElement,
   1.108 +                                        int32_t aNameSpaceID,
   1.109 +                                        nsIAtom* aAttribute,
   1.110 +                                        int32_t aModType)
   1.111 +{
   1.112 +  if (nsAutoMutationBatch::IsBatching() ||
   1.113 +      !ObservesAttr(aElement, aNameSpaceID, aAttribute) ||
   1.114 +      aElement->ChromeOnlyAccess()) {
   1.115 +    return;
   1.116 +  }
   1.117 +
   1.118 +  nsDOMMutationRecord* m =
   1.119 +    Observer()->CurrentRecord(nsGkAtoms::attributes);
   1.120 +
   1.121 +  NS_ASSERTION(!m->mTarget || m->mTarget == aElement,
   1.122 +               "Wrong target!");
   1.123 +  NS_ASSERTION(!m->mAttrName || m->mAttrName == aAttribute,
   1.124 +               "Wrong attribute!");
   1.125 +  if (!m->mTarget) {
   1.126 +    m->mTarget = aElement;
   1.127 +    m->mAttrName = aAttribute;
   1.128 +    if (aNameSpaceID == kNameSpaceID_None) {
   1.129 +      m->mAttrNamespace.SetIsVoid(true);
   1.130 +    } else {
   1.131 +      nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID,
   1.132 +                                                          m->mAttrNamespace);
   1.133 +    }
   1.134 +  }
   1.135 +
   1.136 +  if (AttributeOldValue() && m->mPrevValue.IsVoid()) {
   1.137 +    if (!aElement->GetAttr(aNameSpaceID, aAttribute, m->mPrevValue)) {
   1.138 +      m->mPrevValue.SetIsVoid(true);
   1.139 +    }
   1.140 +  }
   1.141 +}
   1.142 +
   1.143 +void
   1.144 +nsMutationReceiver::CharacterDataWillChange(nsIDocument *aDocument,
   1.145 +                                            nsIContent* aContent,
   1.146 +                                            CharacterDataChangeInfo* aInfo)
   1.147 +{
   1.148 +  if (nsAutoMutationBatch::IsBatching() ||
   1.149 +      !CharacterData() || !(Subtree() || aContent == Target()) ||
   1.150 +      aContent->ChromeOnlyAccess()) {
   1.151 +    return;
   1.152 +  }
   1.153 +  
   1.154 +  nsDOMMutationRecord* m =
   1.155 +    Observer()->CurrentRecord(nsGkAtoms::characterData);
   1.156 + 
   1.157 +  NS_ASSERTION(!m->mTarget || m->mTarget == aContent,
   1.158 +               "Wrong target!");
   1.159 +
   1.160 +  if (!m->mTarget) {
   1.161 +    m->mTarget = aContent;
   1.162 +  }
   1.163 +  if (CharacterDataOldValue() && m->mPrevValue.IsVoid()) { 
   1.164 +    aContent->GetText()->AppendTo(m->mPrevValue);
   1.165 +  }
   1.166 +}
   1.167 +
   1.168 +void
   1.169 +nsMutationReceiver::ContentAppended(nsIDocument* aDocument,
   1.170 +                                    nsIContent* aContainer,
   1.171 +                                    nsIContent* aFirstNewContent,
   1.172 +                                    int32_t aNewIndexInContainer)
   1.173 +{
   1.174 +  nsINode* parent = NODE_FROM(aContainer, aDocument);
   1.175 +  bool wantsChildList = ChildList() && (Subtree() || parent == Target());
   1.176 +  if (!wantsChildList || aFirstNewContent->ChromeOnlyAccess()) {
   1.177 +    return;
   1.178 +  }
   1.179 +
   1.180 +  if (nsAutoMutationBatch::IsBatching()) {
   1.181 +    if (parent == nsAutoMutationBatch::GetBatchTarget()) {
   1.182 +      nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList);
   1.183 +    }
   1.184 +    return;
   1.185 +  }
   1.186 +
   1.187 +  nsDOMMutationRecord* m =
   1.188 +    Observer()->CurrentRecord(nsGkAtoms::childList);
   1.189 +  NS_ASSERTION(!m->mTarget || m->mTarget == parent,
   1.190 +               "Wrong target!");
   1.191 +  if (m->mTarget) {
   1.192 +    // Already handled case.
   1.193 +    return;
   1.194 +  }
   1.195 +  m->mTarget = parent;
   1.196 +  m->mAddedNodes = new nsSimpleContentList(parent);
   1.197 +
   1.198 +  nsINode* n = aFirstNewContent;
   1.199 +  while (n) {
   1.200 +    m->mAddedNodes->AppendElement(static_cast<nsIContent*>(n));
   1.201 +    n = n->GetNextSibling();
   1.202 +  }
   1.203 +  m->mPreviousSibling = aFirstNewContent->GetPreviousSibling();
   1.204 +}
   1.205 +
   1.206 +void
   1.207 +nsMutationReceiver::ContentInserted(nsIDocument* aDocument,
   1.208 +                                    nsIContent* aContainer,
   1.209 +                                    nsIContent* aChild,
   1.210 +                                    int32_t aIndexInContainer)
   1.211 +{
   1.212 +  nsINode* parent = NODE_FROM(aContainer, aDocument);
   1.213 +  bool wantsChildList = ChildList() && (Subtree() || parent == Target());
   1.214 +  if (!wantsChildList || aChild->ChromeOnlyAccess()) {
   1.215 +    return;
   1.216 +  }
   1.217 +
   1.218 +  if (nsAutoMutationBatch::IsBatching()) {
   1.219 +    if (parent == nsAutoMutationBatch::GetBatchTarget()) {
   1.220 +      nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList);
   1.221 +    }
   1.222 +    return;
   1.223 +  }
   1.224 +
   1.225 +  nsDOMMutationRecord* m =
   1.226 +    Observer()->CurrentRecord(nsGkAtoms::childList);
   1.227 +  if (m->mTarget) {
   1.228 +    // Already handled case.
   1.229 +    return;  
   1.230 +  }
   1.231 +  m->mTarget = parent;
   1.232 +  m->mAddedNodes = new nsSimpleContentList(parent);
   1.233 +  m->mAddedNodes->AppendElement(aChild);
   1.234 +  m->mPreviousSibling = aChild->GetPreviousSibling();
   1.235 +  m->mNextSibling = aChild->GetNextSibling();
   1.236 +}
   1.237 +
   1.238 +void
   1.239 +nsMutationReceiver::ContentRemoved(nsIDocument* aDocument,
   1.240 +                                   nsIContent* aContainer,
   1.241 +                                   nsIContent* aChild,
   1.242 +                                   int32_t aIndexInContainer,
   1.243 +                                   nsIContent* aPreviousSibling)
   1.244 +{
   1.245 +  if (aChild->ChromeOnlyAccess()) {
   1.246 +    return;
   1.247 +  }
   1.248 +
   1.249 +  nsINode* parent = NODE_FROM(aContainer, aDocument);
   1.250 +  if (nsAutoMutationBatch::IsBatching()) {
   1.251 +    if (nsAutoMutationBatch::IsRemovalDone()) {
   1.252 +      // This can happen for example if HTML parser parses to
   1.253 +      // context node, but needs to move elements around.
   1.254 +      return;
   1.255 +    }
   1.256 +    if (nsAutoMutationBatch::GetBatchTarget() != parent) {
   1.257 +      return;
   1.258 +    }
   1.259 +
   1.260 +    bool wantsChildList = ChildList() && (Subtree() || parent == Target());
   1.261 +    if (wantsChildList || Subtree()) {
   1.262 +      nsAutoMutationBatch::NodeRemoved(aChild);
   1.263 +      nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList);
   1.264 +    }
   1.265 +
   1.266 +    return;
   1.267 +  }                                                                   
   1.268 +
   1.269 +  if (Subtree()) {
   1.270 +    // Try to avoid creating transient observer if the node
   1.271 +    // already has an observer observing the same set of nodes.
   1.272 +    nsMutationReceiver* orig = GetParent() ? GetParent() : this;
   1.273 +    if (Observer()->GetReceiverFor(aChild, false) != orig) {
   1.274 +      bool transientExists = false;
   1.275 +      nsCOMArray<nsMutationReceiver>* transientReceivers = nullptr;
   1.276 +      Observer()->mTransientReceivers.Get(aChild, &transientReceivers);
   1.277 +      if (!transientReceivers) {
   1.278 +        transientReceivers = new nsCOMArray<nsMutationReceiver>();
   1.279 +        Observer()->mTransientReceivers.Put(aChild, transientReceivers);
   1.280 +      } else {
   1.281 +        for (int32_t i = 0; i < transientReceivers->Count(); ++i) {
   1.282 +          nsMutationReceiver* r = transientReceivers->ObjectAt(i);
   1.283 +          if (r->GetParent() == orig) {
   1.284 +            transientExists = true;
   1.285 +          }
   1.286 +        }
   1.287 +      }
   1.288 +      if (!transientExists) {
   1.289 +        // Make sure the elements which are removed from the
   1.290 +        // subtree are kept in the same observation set.
   1.291 +        transientReceivers->AppendObject(new nsMutationReceiver(aChild, orig));
   1.292 +      }
   1.293 +    }
   1.294 +  }
   1.295 +
   1.296 +  if (ChildList() && (Subtree() || parent == Target())) {
   1.297 +    nsDOMMutationRecord* m =
   1.298 +      Observer()->CurrentRecord(nsGkAtoms::childList);
   1.299 +    if (m->mTarget) {
   1.300 +      // Already handled case.
   1.301 +      return;
   1.302 +    }
   1.303 +    m->mTarget = parent;
   1.304 +    m->mRemovedNodes = new nsSimpleContentList(parent);
   1.305 +    m->mRemovedNodes->AppendElement(aChild);
   1.306 +    m->mPreviousSibling = aPreviousSibling;
   1.307 +    m->mNextSibling = parent->GetChildAt(aIndexInContainer);
   1.308 +  }
   1.309 +  // We need to schedule always, so that after microtask mTransientReceivers
   1.310 +  // can be cleared correctly.
   1.311 +  Observer()->ScheduleForRun();
   1.312 +}
   1.313 +
   1.314 +void nsMutationReceiver::NodeWillBeDestroyed(const nsINode *aNode)
   1.315 +{
   1.316 +  NS_ASSERTION(!mParent, "Shouldn't have mParent here!");
   1.317 +  Disconnect(true);
   1.318 +}
   1.319 +
   1.320 +// Observer
   1.321 +
   1.322 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMMutationObserver)
   1.323 +  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   1.324 +  NS_INTERFACE_MAP_ENTRY(nsISupports)
   1.325 +  NS_INTERFACE_MAP_ENTRY(nsDOMMutationObserver)
   1.326 +NS_INTERFACE_MAP_END
   1.327 +
   1.328 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMMutationObserver)
   1.329 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMMutationObserver)
   1.330 +
   1.331 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMMutationObserver)
   1.332 +
   1.333 +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMMutationObserver)
   1.334 +  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
   1.335 +NS_IMPL_CYCLE_COLLECTION_TRACE_END
   1.336 +
   1.337 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMMutationObserver)
   1.338 +  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   1.339 +  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
   1.340 +  for (int32_t i = 0; i < tmp->mReceivers.Count(); ++i) {
   1.341 +    tmp->mReceivers[i]->Disconnect(false);
   1.342 +  }
   1.343 +  tmp->mReceivers.Clear();
   1.344 +  tmp->ClearPendingRecords();
   1.345 +  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
   1.346 +  // No need to handle mTransientReceivers
   1.347 +  NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   1.348 +
   1.349 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMMutationObserver)
   1.350 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   1.351 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
   1.352 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReceivers)
   1.353 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstPendingMutation)
   1.354 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
   1.355 +  // No need to handle mTransientReceivers
   1.356 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   1.357 +
   1.358 +nsMutationReceiver*
   1.359 +nsDOMMutationObserver::GetReceiverFor(nsINode* aNode, bool aMayCreate)
   1.360 +{
   1.361 +  if (!aMayCreate && !aNode->MayHaveDOMMutationObserver()) {
   1.362 +    return nullptr;
   1.363 +  }
   1.364 +
   1.365 +  for (int32_t i = 0; i < mReceivers.Count(); ++i) {
   1.366 +    if (mReceivers[i]->Target() == aNode) {
   1.367 +      return mReceivers[i];
   1.368 +    }
   1.369 +  }
   1.370 +  if (!aMayCreate) {
   1.371 +    return nullptr;
   1.372 +  }
   1.373 +
   1.374 +  nsMutationReceiver* r = new nsMutationReceiver(aNode, this);
   1.375 +  mReceivers.AppendObject(r);
   1.376 +  return r;
   1.377 +}
   1.378 +
   1.379 +void
   1.380 +nsDOMMutationObserver::RemoveReceiver(nsMutationReceiver* aReceiver)
   1.381 +{
   1.382 +  mReceivers.RemoveObject(aReceiver);
   1.383 +}
   1.384 +
   1.385 +void
   1.386 +nsDOMMutationObserver::GetAllSubtreeObserversFor(nsINode* aNode,
   1.387 +                                                 nsTArray<nsMutationReceiver*>&
   1.388 +                                                   aReceivers)
   1.389 +{
   1.390 +  nsINode* n = aNode;
   1.391 +  while (n) {
   1.392 +    if (n->MayHaveDOMMutationObserver()) {
   1.393 +      nsMutationReceiver* r = GetReceiverFor(n, false);
   1.394 +      if (r && r->Subtree() && !aReceivers.Contains(r)) {
   1.395 +        aReceivers.AppendElement(r);
   1.396 +        // If we've found all the receivers the observer has,
   1.397 +        // no need to search for more.
   1.398 +        if (mReceivers.Count() == int32_t(aReceivers.Length())) {
   1.399 +          return;
   1.400 +        }
   1.401 +      }                                            
   1.402 +      nsCOMArray<nsMutationReceiver>* transientReceivers = nullptr;
   1.403 +      if (mTransientReceivers.Get(n, &transientReceivers) && transientReceivers) {
   1.404 +        for (int32_t i = 0; i < transientReceivers->Count(); ++i) {
   1.405 +          nsMutationReceiver* r = transientReceivers->ObjectAt(i);
   1.406 +          nsMutationReceiver* parent = r->GetParent();
   1.407 +          if (r->Subtree() && parent && !aReceivers.Contains(parent)) {
   1.408 +            aReceivers.AppendElement(parent);
   1.409 +          }
   1.410 +        }
   1.411 +        if (mReceivers.Count() == int32_t(aReceivers.Length())) {
   1.412 +          return;
   1.413 +        }
   1.414 +      }
   1.415 +    }
   1.416 +    n = n->GetParentNode();
   1.417 +  }
   1.418 +}
   1.419 +
   1.420 +void
   1.421 +nsDOMMutationObserver::ScheduleForRun()
   1.422 +{
   1.423 +  nsDOMMutationObserver::AddCurrentlyHandlingObserver(this);
   1.424 +
   1.425 +  if (mWaitingForRun) {
   1.426 +    return;
   1.427 +  }
   1.428 +  mWaitingForRun = true;
   1.429 +  RescheduleForRun();
   1.430 +}
   1.431 +
   1.432 +void
   1.433 +nsDOMMutationObserver::RescheduleForRun()
   1.434 +{
   1.435 +  if (!sScheduledMutationObservers) {
   1.436 +    sScheduledMutationObservers = new nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>;
   1.437 +  }
   1.438 +
   1.439 +  bool didInsert = false;
   1.440 +  for (uint32_t i = 0; i < sScheduledMutationObservers->Length(); ++i) {
   1.441 +    if (static_cast<nsDOMMutationObserver*>((*sScheduledMutationObservers)[i])
   1.442 +          ->mId > mId) {
   1.443 +      sScheduledMutationObservers->InsertElementAt(i, this);
   1.444 +      didInsert = true;
   1.445 +      break;
   1.446 +    }
   1.447 +  }
   1.448 +  if (!didInsert) {
   1.449 +    sScheduledMutationObservers->AppendElement(this);
   1.450 +  }
   1.451 +}
   1.452 +
   1.453 +void
   1.454 +nsDOMMutationObserver::Observe(nsINode& aTarget,
   1.455 +                               const mozilla::dom::MutationObserverInit& aOptions,
   1.456 +                               mozilla::ErrorResult& aRv)
   1.457 +{
   1.458 +
   1.459 +  if (!(aOptions.mChildList || aOptions.mAttributes || aOptions.mCharacterData)) {
   1.460 +    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
   1.461 +    return;
   1.462 +  }
   1.463 +  if (aOptions.mAttributeOldValue && !aOptions.mAttributes) {
   1.464 +    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
   1.465 +    return;
   1.466 +  }
   1.467 +  if (aOptions.mCharacterDataOldValue && !aOptions.mCharacterData) {
   1.468 +    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
   1.469 +    return;
   1.470 +  }
   1.471 +
   1.472 +  nsCOMArray<nsIAtom> filters;
   1.473 +  bool allAttrs = true;
   1.474 +  if (aOptions.mAttributeFilter.WasPassed()) {
   1.475 +    allAttrs = false;
   1.476 +    const mozilla::dom::Sequence<nsString>& filtersAsString =
   1.477 +      aOptions.mAttributeFilter.Value();
   1.478 +    uint32_t len = filtersAsString.Length();
   1.479 +
   1.480 +    if (len != 0 && !aOptions.mAttributes) {
   1.481 +      aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
   1.482 +      return;
   1.483 +    }
   1.484 +    filters.SetCapacity(len);
   1.485 +
   1.486 +    for (uint32_t i = 0; i < len; ++i) {
   1.487 +      nsCOMPtr<nsIAtom> a = do_GetAtom(filtersAsString[i]);
   1.488 +      filters.AppendObject(a);
   1.489 +    }
   1.490 +  }
   1.491 +
   1.492 +  nsMutationReceiver* r = GetReceiverFor(&aTarget, true);
   1.493 +  r->SetChildList(aOptions.mChildList);
   1.494 +  r->SetAttributes(aOptions.mAttributes);
   1.495 +  r->SetCharacterData(aOptions.mCharacterData);
   1.496 +  r->SetSubtree(aOptions.mSubtree);
   1.497 +  r->SetAttributeOldValue(aOptions.mAttributeOldValue);
   1.498 +  r->SetCharacterDataOldValue(aOptions.mCharacterDataOldValue);
   1.499 +  r->SetAttributeFilter(filters);
   1.500 +  r->SetAllAttributes(allAttrs);
   1.501 +  r->RemoveClones();
   1.502 +
   1.503 +#ifdef DEBUG
   1.504 +  for (int32_t i = 0; i < mReceivers.Count(); ++i) {
   1.505 +    NS_WARN_IF_FALSE(mReceivers[i]->Target(),
   1.506 +                     "All the receivers should have a target!");
   1.507 +  }
   1.508 +#endif
   1.509 +}
   1.510 +
   1.511 +void
   1.512 +nsDOMMutationObserver::Disconnect()
   1.513 +{
   1.514 +  for (int32_t i = 0; i < mReceivers.Count(); ++i) {
   1.515 +    mReceivers[i]->Disconnect(false);
   1.516 +  }
   1.517 +  mReceivers.Clear();
   1.518 +  mCurrentMutations.Clear();
   1.519 +  ClearPendingRecords();
   1.520 +}
   1.521 +
   1.522 +void
   1.523 +nsDOMMutationObserver::TakeRecords(
   1.524 +                         nsTArray<nsRefPtr<nsDOMMutationRecord> >& aRetVal)
   1.525 +{
   1.526 +  aRetVal.Clear();
   1.527 +  aRetVal.SetCapacity(mPendingMutationCount);
   1.528 +  nsRefPtr<nsDOMMutationRecord> current;
   1.529 +  current.swap(mFirstPendingMutation);
   1.530 +  for (uint32_t i = 0; i < mPendingMutationCount; ++i) {
   1.531 +    nsRefPtr<nsDOMMutationRecord> next;
   1.532 +    current->mNext.swap(next);
   1.533 +    *aRetVal.AppendElement() = current.forget();
   1.534 +    current.swap(next);
   1.535 +  }
   1.536 +  ClearPendingRecords();
   1.537 +}
   1.538 +
   1.539 +void
   1.540 +nsDOMMutationObserver::GetObservingInfo(nsTArray<Nullable<MutationObservingInfo> >& aResult)
   1.541 +{
   1.542 +  aResult.SetCapacity(mReceivers.Count());
   1.543 +  for (int32_t i = 0; i < mReceivers.Count(); ++i) {
   1.544 +    MutationObservingInfo& info = aResult.AppendElement()->SetValue();
   1.545 +    nsMutationReceiver* mr = mReceivers[i];
   1.546 +    info.mChildList = mr->ChildList();
   1.547 +    info.mAttributes = mr->Attributes();
   1.548 +    info.mCharacterData = mr->CharacterData();
   1.549 +    info.mSubtree = mr->Subtree();
   1.550 +    info.mAttributeOldValue = mr->AttributeOldValue();
   1.551 +    info.mCharacterDataOldValue = mr->CharacterDataOldValue();
   1.552 +    nsCOMArray<nsIAtom>& filters = mr->AttributeFilter();
   1.553 +    if (filters.Count()) {
   1.554 +      info.mAttributeFilter.Construct();
   1.555 +      mozilla::dom::Sequence<nsString>& filtersAsStrings =
   1.556 +        info.mAttributeFilter.Value();
   1.557 +      for (int32_t j = 0; j < filters.Count(); ++j) {
   1.558 +        filtersAsStrings.AppendElement(nsDependentAtomString(filters[j]));
   1.559 +      }
   1.560 +    }
   1.561 +    info.mObservedNode = mr->Target();
   1.562 +  }
   1.563 +}
   1.564 +
   1.565 +// static
   1.566 +already_AddRefed<nsDOMMutationObserver>
   1.567 +nsDOMMutationObserver::Constructor(const mozilla::dom::GlobalObject& aGlobal,
   1.568 +                                   mozilla::dom::MutationCallback& aCb,
   1.569 +                                   mozilla::ErrorResult& aRv)
   1.570 +{
   1.571 +  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
   1.572 +  if (!window) {
   1.573 +    aRv.Throw(NS_ERROR_FAILURE);
   1.574 +    return nullptr;
   1.575 +  }
   1.576 +  MOZ_ASSERT(window->IsInnerWindow());
   1.577 +  nsRefPtr<nsDOMMutationObserver> observer =
   1.578 +    new nsDOMMutationObserver(window.forget(), aCb);
   1.579 +  return observer.forget();
   1.580 +}
   1.581 +
   1.582 +void
   1.583 +nsDOMMutationObserver::HandleMutation()
   1.584 +{
   1.585 +  NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), "Whaat!");
   1.586 +  NS_ASSERTION(mCurrentMutations.IsEmpty(),
   1.587 +               "Still generating MutationRecords?");
   1.588 +
   1.589 +  mWaitingForRun = false;
   1.590 +
   1.591 +  for (int32_t i = 0; i < mReceivers.Count(); ++i) {
   1.592 +    mReceivers[i]->RemoveClones();
   1.593 +  }
   1.594 +  mTransientReceivers.Clear();
   1.595 +
   1.596 +  nsPIDOMWindow* outer = mOwner->GetOuterWindow();
   1.597 +  if (!mPendingMutationCount || !outer ||
   1.598 +      outer->GetCurrentInnerWindow() != mOwner) {
   1.599 +    ClearPendingRecords();
   1.600 +    return;
   1.601 +  }
   1.602 +
   1.603 +  mozilla::dom::Sequence<mozilla::dom::OwningNonNull<nsDOMMutationRecord> >
   1.604 +    mutations;
   1.605 +  if (mutations.SetCapacity(mPendingMutationCount)) {
   1.606 +    // We can't use TakeRecords easily here, because it deals with a
   1.607 +    // different type of array, and we want to optimize out any extra copying.
   1.608 +    nsRefPtr<nsDOMMutationRecord> current;
   1.609 +    current.swap(mFirstPendingMutation);
   1.610 +    for (uint32_t i = 0; i < mPendingMutationCount; ++i) {
   1.611 +      nsRefPtr<nsDOMMutationRecord> next;
   1.612 +      current->mNext.swap(next);
   1.613 +      *mutations.AppendElement() = current;
   1.614 +      current.swap(next);
   1.615 +    }
   1.616 +  }
   1.617 +  ClearPendingRecords();
   1.618 +
   1.619 +  mozilla::ErrorResult rv;
   1.620 +  mCallback->Call(this, mutations, *this, rv);
   1.621 +}
   1.622 +
   1.623 +class AsyncMutationHandler : public nsRunnable
   1.624 +{
   1.625 +public:
   1.626 +  NS_IMETHOD Run()
   1.627 +  {
   1.628 +    nsDOMMutationObserver::HandleMutations();
   1.629 +    return NS_OK;
   1.630 +  }
   1.631 +};
   1.632 +
   1.633 +void
   1.634 +nsDOMMutationObserver::HandleMutationsInternal()
   1.635 +{
   1.636 +  if (!nsContentUtils::IsSafeToRunScript()) {
   1.637 +    nsContentUtils::AddScriptRunner(new AsyncMutationHandler());
   1.638 +    return;
   1.639 +  }
   1.640 +  static nsRefPtr<nsDOMMutationObserver> sCurrentObserver;
   1.641 +  if (sCurrentObserver && !sCurrentObserver->Suppressed()) {
   1.642 +    // In normal cases sScheduledMutationObservers will be handled
   1.643 +    // after previous mutations are handled. But in case some
   1.644 +    // callback calls a sync API, which spins the eventloop, we need to still
   1.645 +    // process other mutations happening during that sync call.
   1.646 +    // This does *not* catch all cases, but should work for stuff running
   1.647 +    // in separate tabs.
   1.648 +    return;
   1.649 +  }
   1.650 +
   1.651 +  nsTArray<nsRefPtr<nsDOMMutationObserver> >* suppressedObservers = nullptr;
   1.652 +
   1.653 +  while (sScheduledMutationObservers) {
   1.654 +    nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>* observers =
   1.655 +      sScheduledMutationObservers;
   1.656 +    sScheduledMutationObservers = nullptr;
   1.657 +    for (uint32_t i = 0; i < observers->Length(); ++i) {
   1.658 +      sCurrentObserver = static_cast<nsDOMMutationObserver*>((*observers)[i]);
   1.659 +      if (!sCurrentObserver->Suppressed()) {
   1.660 +        sCurrentObserver->HandleMutation();
   1.661 +      } else {
   1.662 +        if (!suppressedObservers) {
   1.663 +          suppressedObservers = new nsTArray<nsRefPtr<nsDOMMutationObserver> >;
   1.664 +        }
   1.665 +        if (!suppressedObservers->Contains(sCurrentObserver)) {
   1.666 +          suppressedObservers->AppendElement(sCurrentObserver);
   1.667 +        }
   1.668 +      }
   1.669 +    }
   1.670 +    delete observers;
   1.671 +  }
   1.672 +
   1.673 +  if (suppressedObservers) {
   1.674 +    for (uint32_t i = 0; i < suppressedObservers->Length(); ++i) {
   1.675 +      static_cast<nsDOMMutationObserver*>(suppressedObservers->ElementAt(i))->
   1.676 +        RescheduleForRun();
   1.677 +    }
   1.678 +    delete suppressedObservers;
   1.679 +    suppressedObservers = nullptr;
   1.680 +  }
   1.681 +  sCurrentObserver = nullptr;
   1.682 +}
   1.683 +
   1.684 +nsDOMMutationRecord*
   1.685 +nsDOMMutationObserver::CurrentRecord(nsIAtom* aType)
   1.686 +{
   1.687 +  NS_ASSERTION(sMutationLevel > 0, "Unexpected mutation level!");
   1.688 +
   1.689 +  while (mCurrentMutations.Length() < sMutationLevel) {
   1.690 +    mCurrentMutations.AppendElement(static_cast<nsDOMMutationRecord*>(nullptr));
   1.691 +  }
   1.692 +
   1.693 +  uint32_t last = sMutationLevel - 1;
   1.694 +  if (!mCurrentMutations[last]) {
   1.695 +    nsRefPtr<nsDOMMutationRecord> r = new nsDOMMutationRecord(aType, GetParentObject());
   1.696 +    mCurrentMutations[last] = r;
   1.697 +    AppendMutationRecord(r.forget());
   1.698 +    ScheduleForRun();
   1.699 +  }
   1.700 +
   1.701 +  NS_ASSERTION(mCurrentMutations[last]->mType == aType,
   1.702 +               "Unexpected MutationRecord type!");
   1.703 +
   1.704 +  return mCurrentMutations[last];
   1.705 +}
   1.706 +
   1.707 +nsDOMMutationObserver::~nsDOMMutationObserver()
   1.708 +{
   1.709 +  for (int32_t i = 0; i < mReceivers.Count(); ++i) {
   1.710 +    mReceivers[i]->RemoveClones();
   1.711 +  }
   1.712 +}
   1.713 +
   1.714 +void
   1.715 +nsDOMMutationObserver::EnterMutationHandling()
   1.716 +{
   1.717 +  ++sMutationLevel;
   1.718 +}
   1.719 +
   1.720 +// Leave the current mutation level (there can be several levels if in case
   1.721 +// of nested calls to the nsIMutationObserver methods).
   1.722 +// The most recent mutation record is removed from mCurrentMutations, so
   1.723 +// that is doesn't get modified anymore by receivers.
   1.724 +void
   1.725 +nsDOMMutationObserver::LeaveMutationHandling()
   1.726 +{
   1.727 +  if (sCurrentlyHandlingObservers &&
   1.728 +      sCurrentlyHandlingObservers->Length() == sMutationLevel) {
   1.729 +    nsTArray<nsRefPtr<nsDOMMutationObserver> >& obs =
   1.730 +      sCurrentlyHandlingObservers->ElementAt(sMutationLevel - 1);
   1.731 +    for (uint32_t i = 0; i < obs.Length(); ++i) {
   1.732 +      nsDOMMutationObserver* o =
   1.733 +        static_cast<nsDOMMutationObserver*>(obs[i]);
   1.734 +      if (o->mCurrentMutations.Length() == sMutationLevel) {
   1.735 +        // It is already in pending mutations.
   1.736 +        o->mCurrentMutations.RemoveElementAt(sMutationLevel - 1);
   1.737 +      }
   1.738 +    }
   1.739 +    sCurrentlyHandlingObservers->RemoveElementAt(sMutationLevel - 1);
   1.740 +  }
   1.741 +  --sMutationLevel;
   1.742 +}
   1.743 +
   1.744 +void
   1.745 +nsDOMMutationObserver::AddCurrentlyHandlingObserver(nsDOMMutationObserver* aObserver)
   1.746 +{
   1.747 +  NS_ASSERTION(sMutationLevel > 0, "Unexpected mutation level!");
   1.748 +
   1.749 +  if (!sCurrentlyHandlingObservers) {
   1.750 +    sCurrentlyHandlingObservers =
   1.751 +      new nsAutoTArray<nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>, 4>;
   1.752 +  }
   1.753 +
   1.754 +  while (sCurrentlyHandlingObservers->Length() < sMutationLevel) {
   1.755 +    sCurrentlyHandlingObservers->InsertElementAt(
   1.756 +      sCurrentlyHandlingObservers->Length());
   1.757 +  }
   1.758 +
   1.759 +  uint32_t last = sMutationLevel - 1;
   1.760 +  if (!sCurrentlyHandlingObservers->ElementAt(last).Contains(aObserver)) {
   1.761 +    sCurrentlyHandlingObservers->ElementAt(last).AppendElement(aObserver);
   1.762 +  }
   1.763 +}
   1.764 +
   1.765 +void
   1.766 +nsDOMMutationObserver::Shutdown()
   1.767 +{
   1.768 +  delete sCurrentlyHandlingObservers;
   1.769 +  sCurrentlyHandlingObservers = nullptr;
   1.770 +  delete sScheduledMutationObservers;
   1.771 +  sScheduledMutationObservers = nullptr;
   1.772 +}
   1.773 +
   1.774 +nsAutoMutationBatch*
   1.775 +nsAutoMutationBatch::sCurrentBatch = nullptr;
   1.776 +
   1.777 +void
   1.778 +nsAutoMutationBatch::Done()
   1.779 +{
   1.780 +  if (sCurrentBatch != this) {
   1.781 +    return;
   1.782 +  }
   1.783 +
   1.784 +  sCurrentBatch = mPreviousBatch;
   1.785 +  if (mObservers.IsEmpty()) {
   1.786 +    nsDOMMutationObserver::LeaveMutationHandling();
   1.787 +    // Nothing to do.
   1.788 +    return;
   1.789 +  }
   1.790 +
   1.791 +  uint32_t len = mObservers.Length();
   1.792 +  for (uint32_t i = 0; i < len; ++i) {
   1.793 +    nsDOMMutationObserver* ob = mObservers[i].mObserver;
   1.794 +    bool wantsChildList = mObservers[i].mWantsChildList;
   1.795 +
   1.796 +    nsRefPtr<nsSimpleContentList> removedList;
   1.797 +    if (wantsChildList) {
   1.798 +      removedList = new nsSimpleContentList(mBatchTarget);
   1.799 +    }
   1.800 +
   1.801 +    nsTArray<nsMutationReceiver*> allObservers;
   1.802 +    ob->GetAllSubtreeObserversFor(mBatchTarget, allObservers);
   1.803 +
   1.804 +    int32_t j = mFromFirstToLast ? 0 : mRemovedNodes.Length() - 1;
   1.805 +    int32_t end = mFromFirstToLast ? mRemovedNodes.Length() : -1;
   1.806 +    for (; j != end; mFromFirstToLast ? ++j : --j) {
   1.807 +      nsCOMPtr<nsIContent> removed = mRemovedNodes[j];
   1.808 +      if (removedList) {
   1.809 +        removedList->AppendElement(removed);
   1.810 +      }
   1.811 +
   1.812 +      if (allObservers.Length()) {
   1.813 +        nsCOMArray<nsMutationReceiver>* transientReceivers = nullptr;
   1.814 +        ob->mTransientReceivers.Get(removed, &transientReceivers);
   1.815 +        if (!transientReceivers) {
   1.816 +          transientReceivers = new nsCOMArray<nsMutationReceiver>();
   1.817 +          ob->mTransientReceivers.Put(removed, transientReceivers);
   1.818 +        }
   1.819 +        for (uint32_t k = 0; k < allObservers.Length(); ++k) {
   1.820 +          nsMutationReceiver* r = allObservers[k];
   1.821 +          nsMutationReceiver* orig = r->GetParent() ? r->GetParent() : r;
   1.822 +          if (ob->GetReceiverFor(removed, false) != orig) {
   1.823 +            // Make sure the elements which are removed from the
   1.824 +            // subtree are kept in the same observation set.
   1.825 +            transientReceivers->AppendObject(new nsMutationReceiver(removed, orig));
   1.826 +          }
   1.827 +        }
   1.828 +      }
   1.829 +    }
   1.830 +    if (wantsChildList && (mRemovedNodes.Length() || mAddedNodes.Length())) {
   1.831 +      nsRefPtr<nsSimpleContentList> addedList =
   1.832 +        new nsSimpleContentList(mBatchTarget);
   1.833 +      for (uint32_t i = 0; i < mAddedNodes.Length(); ++i) {
   1.834 +        addedList->AppendElement(mAddedNodes[i]);
   1.835 +      }
   1.836 +      nsRefPtr<nsDOMMutationRecord> m =
   1.837 +        new nsDOMMutationRecord(nsGkAtoms::childList,
   1.838 +                                ob->GetParentObject());
   1.839 +      m->mTarget = mBatchTarget;
   1.840 +      m->mRemovedNodes = removedList;
   1.841 +      m->mAddedNodes = addedList;
   1.842 +      m->mPreviousSibling = mPrevSibling;
   1.843 +      m->mNextSibling = mNextSibling;
   1.844 +      ob->AppendMutationRecord(m.forget());
   1.845 +    }
   1.846 +    // Always schedule the observer so that transient receivers are
   1.847 +    // removed correctly.
   1.848 +    ob->ScheduleForRun();
   1.849 +  }
   1.850 +  nsDOMMutationObserver::LeaveMutationHandling();
   1.851 +}

mercurial