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.)

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

mercurial