|
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/. */ |
|
6 |
|
7 #include "nsDOMMutationObserver.h" |
|
8 |
|
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" |
|
17 |
|
18 nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>* |
|
19 nsDOMMutationObserver::sScheduledMutationObservers = nullptr; |
|
20 |
|
21 nsDOMMutationObserver* nsDOMMutationObserver::sCurrentObserver = nullptr; |
|
22 |
|
23 uint32_t nsDOMMutationObserver::sMutationLevel = 0; |
|
24 uint64_t nsDOMMutationObserver::sCount = 0; |
|
25 |
|
26 nsAutoTArray<nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>, 4>* |
|
27 nsDOMMutationObserver::sCurrentlyHandlingObservers = nullptr; |
|
28 |
|
29 nsINodeList* |
|
30 nsDOMMutationRecord::AddedNodes() |
|
31 { |
|
32 if (!mAddedNodes) { |
|
33 mAddedNodes = new nsSimpleContentList(mTarget); |
|
34 } |
|
35 return mAddedNodes; |
|
36 } |
|
37 |
|
38 nsINodeList* |
|
39 nsDOMMutationRecord::RemovedNodes() |
|
40 { |
|
41 if (!mRemovedNodes) { |
|
42 mRemovedNodes = new nsSimpleContentList(mTarget); |
|
43 } |
|
44 return mRemovedNodes; |
|
45 } |
|
46 |
|
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 |
|
51 |
|
52 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMMutationRecord) |
|
53 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMMutationRecord) |
|
54 |
|
55 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_7(nsDOMMutationRecord, |
|
56 mTarget, |
|
57 mPreviousSibling, mNextSibling, |
|
58 mAddedNodes, mRemovedNodes, |
|
59 mNext, mOwner) |
|
60 |
|
61 // Observer |
|
62 |
|
63 NS_IMPL_ADDREF(nsMutationReceiver) |
|
64 NS_IMPL_RELEASE(nsMutationReceiver) |
|
65 |
|
66 NS_INTERFACE_MAP_BEGIN(nsMutationReceiver) |
|
67 NS_INTERFACE_MAP_ENTRY(nsISupports) |
|
68 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) |
|
69 NS_INTERFACE_MAP_END |
|
70 |
|
71 nsMutationReceiver::nsMutationReceiver(nsINode* aTarget, |
|
72 nsDOMMutationObserver* aObserver) |
|
73 : nsMutationReceiverBase(aTarget, aObserver) |
|
74 { |
|
75 mTarget->BindObject(aObserver); |
|
76 } |
|
77 |
|
78 void |
|
79 nsMutationReceiver::Disconnect(bool aRemoveFromObserver) |
|
80 { |
|
81 if (mRegisterTarget) { |
|
82 mRegisterTarget->RemoveMutationObserver(this); |
|
83 mRegisterTarget = nullptr; |
|
84 } |
|
85 |
|
86 mParent = nullptr; |
|
87 nsINode* target = mTarget; |
|
88 mTarget = nullptr; |
|
89 nsDOMMutationObserver* observer = mObserver; |
|
90 mObserver = nullptr; |
|
91 RemoveClones(); |
|
92 |
|
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 } |
|
101 |
|
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 } |
|
114 |
|
115 nsDOMMutationRecord* m = |
|
116 Observer()->CurrentRecord(nsGkAtoms::attributes); |
|
117 |
|
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 } |
|
132 |
|
133 if (AttributeOldValue() && m->mPrevValue.IsVoid()) { |
|
134 if (!aElement->GetAttr(aNameSpaceID, aAttribute, m->mPrevValue)) { |
|
135 m->mPrevValue.SetIsVoid(true); |
|
136 } |
|
137 } |
|
138 } |
|
139 |
|
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 } |
|
150 |
|
151 nsDOMMutationRecord* m = |
|
152 Observer()->CurrentRecord(nsGkAtoms::characterData); |
|
153 |
|
154 NS_ASSERTION(!m->mTarget || m->mTarget == aContent, |
|
155 "Wrong target!"); |
|
156 |
|
157 if (!m->mTarget) { |
|
158 m->mTarget = aContent; |
|
159 } |
|
160 if (CharacterDataOldValue() && m->mPrevValue.IsVoid()) { |
|
161 aContent->GetText()->AppendTo(m->mPrevValue); |
|
162 } |
|
163 } |
|
164 |
|
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 } |
|
176 |
|
177 if (nsAutoMutationBatch::IsBatching()) { |
|
178 if (parent == nsAutoMutationBatch::GetBatchTarget()) { |
|
179 nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList); |
|
180 } |
|
181 return; |
|
182 } |
|
183 |
|
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); |
|
194 |
|
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 } |
|
202 |
|
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 } |
|
214 |
|
215 if (nsAutoMutationBatch::IsBatching()) { |
|
216 if (parent == nsAutoMutationBatch::GetBatchTarget()) { |
|
217 nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList); |
|
218 } |
|
219 return; |
|
220 } |
|
221 |
|
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 } |
|
234 |
|
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 } |
|
245 |
|
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 } |
|
256 |
|
257 bool wantsChildList = ChildList() && (Subtree() || parent == Target()); |
|
258 if (wantsChildList || Subtree()) { |
|
259 nsAutoMutationBatch::NodeRemoved(aChild); |
|
260 nsAutoMutationBatch::UpdateObserver(Observer(), wantsChildList); |
|
261 } |
|
262 |
|
263 return; |
|
264 } |
|
265 |
|
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 } |
|
292 |
|
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 } |
|
310 |
|
311 void nsMutationReceiver::NodeWillBeDestroyed(const nsINode *aNode) |
|
312 { |
|
313 NS_ASSERTION(!mParent, "Shouldn't have mParent here!"); |
|
314 Disconnect(true); |
|
315 } |
|
316 |
|
317 // Observer |
|
318 |
|
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 |
|
324 |
|
325 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMMutationObserver) |
|
326 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMMutationObserver) |
|
327 |
|
328 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMMutationObserver) |
|
329 |
|
330 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMMutationObserver) |
|
331 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER |
|
332 NS_IMPL_CYCLE_COLLECTION_TRACE_END |
|
333 |
|
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 |
|
345 |
|
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 |
|
354 |
|
355 nsMutationReceiver* |
|
356 nsDOMMutationObserver::GetReceiverFor(nsINode* aNode, bool aMayCreate) |
|
357 { |
|
358 if (!aMayCreate && !aNode->MayHaveDOMMutationObserver()) { |
|
359 return nullptr; |
|
360 } |
|
361 |
|
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 } |
|
370 |
|
371 nsMutationReceiver* r = new nsMutationReceiver(aNode, this); |
|
372 mReceivers.AppendObject(r); |
|
373 return r; |
|
374 } |
|
375 |
|
376 void |
|
377 nsDOMMutationObserver::RemoveReceiver(nsMutationReceiver* aReceiver) |
|
378 { |
|
379 mReceivers.RemoveObject(aReceiver); |
|
380 } |
|
381 |
|
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 } |
|
416 |
|
417 void |
|
418 nsDOMMutationObserver::ScheduleForRun() |
|
419 { |
|
420 nsDOMMutationObserver::AddCurrentlyHandlingObserver(this); |
|
421 |
|
422 if (mWaitingForRun) { |
|
423 return; |
|
424 } |
|
425 mWaitingForRun = true; |
|
426 RescheduleForRun(); |
|
427 } |
|
428 |
|
429 void |
|
430 nsDOMMutationObserver::RescheduleForRun() |
|
431 { |
|
432 if (!sScheduledMutationObservers) { |
|
433 sScheduledMutationObservers = new nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>; |
|
434 } |
|
435 |
|
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 } |
|
449 |
|
450 void |
|
451 nsDOMMutationObserver::Observe(nsINode& aTarget, |
|
452 const mozilla::dom::MutationObserverInit& aOptions, |
|
453 mozilla::ErrorResult& aRv) |
|
454 { |
|
455 |
|
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 } |
|
468 |
|
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(); |
|
476 |
|
477 if (len != 0 && !aOptions.mAttributes) { |
|
478 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); |
|
479 return; |
|
480 } |
|
481 filters.SetCapacity(len); |
|
482 |
|
483 for (uint32_t i = 0; i < len; ++i) { |
|
484 nsCOMPtr<nsIAtom> a = do_GetAtom(filtersAsString[i]); |
|
485 filters.AppendObject(a); |
|
486 } |
|
487 } |
|
488 |
|
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(); |
|
499 |
|
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 } |
|
507 |
|
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 } |
|
518 |
|
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 } |
|
535 |
|
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 } |
|
561 |
|
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 } |
|
578 |
|
579 void |
|
580 nsDOMMutationObserver::HandleMutation() |
|
581 { |
|
582 NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), "Whaat!"); |
|
583 NS_ASSERTION(mCurrentMutations.IsEmpty(), |
|
584 "Still generating MutationRecords?"); |
|
585 |
|
586 mWaitingForRun = false; |
|
587 |
|
588 for (int32_t i = 0; i < mReceivers.Count(); ++i) { |
|
589 mReceivers[i]->RemoveClones(); |
|
590 } |
|
591 mTransientReceivers.Clear(); |
|
592 |
|
593 nsPIDOMWindow* outer = mOwner->GetOuterWindow(); |
|
594 if (!mPendingMutationCount || !outer || |
|
595 outer->GetCurrentInnerWindow() != mOwner) { |
|
596 ClearPendingRecords(); |
|
597 return; |
|
598 } |
|
599 |
|
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(); |
|
615 |
|
616 mozilla::ErrorResult rv; |
|
617 mCallback->Call(this, mutations, *this, rv); |
|
618 } |
|
619 |
|
620 class AsyncMutationHandler : public nsRunnable |
|
621 { |
|
622 public: |
|
623 NS_IMETHOD Run() |
|
624 { |
|
625 nsDOMMutationObserver::HandleMutations(); |
|
626 return NS_OK; |
|
627 } |
|
628 }; |
|
629 |
|
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 } |
|
647 |
|
648 nsTArray<nsRefPtr<nsDOMMutationObserver> >* suppressedObservers = nullptr; |
|
649 |
|
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 } |
|
669 |
|
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 } |
|
680 |
|
681 nsDOMMutationRecord* |
|
682 nsDOMMutationObserver::CurrentRecord(nsIAtom* aType) |
|
683 { |
|
684 NS_ASSERTION(sMutationLevel > 0, "Unexpected mutation level!"); |
|
685 |
|
686 while (mCurrentMutations.Length() < sMutationLevel) { |
|
687 mCurrentMutations.AppendElement(static_cast<nsDOMMutationRecord*>(nullptr)); |
|
688 } |
|
689 |
|
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 } |
|
697 |
|
698 NS_ASSERTION(mCurrentMutations[last]->mType == aType, |
|
699 "Unexpected MutationRecord type!"); |
|
700 |
|
701 return mCurrentMutations[last]; |
|
702 } |
|
703 |
|
704 nsDOMMutationObserver::~nsDOMMutationObserver() |
|
705 { |
|
706 for (int32_t i = 0; i < mReceivers.Count(); ++i) { |
|
707 mReceivers[i]->RemoveClones(); |
|
708 } |
|
709 } |
|
710 |
|
711 void |
|
712 nsDOMMutationObserver::EnterMutationHandling() |
|
713 { |
|
714 ++sMutationLevel; |
|
715 } |
|
716 |
|
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 } |
|
740 |
|
741 void |
|
742 nsDOMMutationObserver::AddCurrentlyHandlingObserver(nsDOMMutationObserver* aObserver) |
|
743 { |
|
744 NS_ASSERTION(sMutationLevel > 0, "Unexpected mutation level!"); |
|
745 |
|
746 if (!sCurrentlyHandlingObservers) { |
|
747 sCurrentlyHandlingObservers = |
|
748 new nsAutoTArray<nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>, 4>; |
|
749 } |
|
750 |
|
751 while (sCurrentlyHandlingObservers->Length() < sMutationLevel) { |
|
752 sCurrentlyHandlingObservers->InsertElementAt( |
|
753 sCurrentlyHandlingObservers->Length()); |
|
754 } |
|
755 |
|
756 uint32_t last = sMutationLevel - 1; |
|
757 if (!sCurrentlyHandlingObservers->ElementAt(last).Contains(aObserver)) { |
|
758 sCurrentlyHandlingObservers->ElementAt(last).AppendElement(aObserver); |
|
759 } |
|
760 } |
|
761 |
|
762 void |
|
763 nsDOMMutationObserver::Shutdown() |
|
764 { |
|
765 delete sCurrentlyHandlingObservers; |
|
766 sCurrentlyHandlingObservers = nullptr; |
|
767 delete sScheduledMutationObservers; |
|
768 sScheduledMutationObservers = nullptr; |
|
769 } |
|
770 |
|
771 nsAutoMutationBatch* |
|
772 nsAutoMutationBatch::sCurrentBatch = nullptr; |
|
773 |
|
774 void |
|
775 nsAutoMutationBatch::Done() |
|
776 { |
|
777 if (sCurrentBatch != this) { |
|
778 return; |
|
779 } |
|
780 |
|
781 sCurrentBatch = mPreviousBatch; |
|
782 if (mObservers.IsEmpty()) { |
|
783 nsDOMMutationObserver::LeaveMutationHandling(); |
|
784 // Nothing to do. |
|
785 return; |
|
786 } |
|
787 |
|
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; |
|
792 |
|
793 nsRefPtr<nsSimpleContentList> removedList; |
|
794 if (wantsChildList) { |
|
795 removedList = new nsSimpleContentList(mBatchTarget); |
|
796 } |
|
797 |
|
798 nsTArray<nsMutationReceiver*> allObservers; |
|
799 ob->GetAllSubtreeObserversFor(mBatchTarget, allObservers); |
|
800 |
|
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 } |
|
808 |
|
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 } |