From f280dd5f2b6cd8d4e91e048ea6b349f7ed96d48a Mon Sep 17 00:00:00 2001 From: John Messerly Date: Mon, 22 Sep 2014 21:00:47 -0700 Subject: [PATCH] always free transient observers at end of microtask --- src/MutationObserver.js | 24 +++++++++++++---------- test/js/MutationObserver/transient.js | 28 +++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/MutationObserver.js b/src/MutationObserver.js index 6ac797b..8824a94 100644 --- a/src/MutationObserver.js +++ b/src/MutationObserver.js @@ -15,7 +15,13 @@ var globalMutationObservers = []; var isScheduled = false; - function scheduleCallback() { + function scheduleCallback(observer) { + if (observer.scheduled_) + return; + + observer.scheduled_ = true; + globalMutationObservers.push(observer); + if (isScheduled) return; setEndOfMicrotask(notifyObservers); @@ -35,6 +41,7 @@ for (var i = 0; i < notifyList.length; i++) { var mo = notifyList[i]; + mo.scheduled_ = false; var queue = mo.takeRecords(); removeTransientObserversFor(mo); if (queue.length) { @@ -150,8 +157,6 @@ } } - var anyObserversEnqueued = false; - // 4. for (var uid in interestedObservers) { var observer = interestedObservers[uid]; @@ -184,15 +189,9 @@ record.oldValue = associatedStrings[uid]; // 8. - if (!observer.records_.length) { - globalMutationObservers.push(observer); - anyObserversEnqueued = true; - } + scheduleCallback(observer); observer.records_.push(record); } - - if (anyObserversEnqueued) - scheduleCallback(); } var slice = Array.prototype.slice; @@ -255,6 +254,7 @@ this.nodes_ = []; this.records_ = []; this.uid_ = ++uidCounter; + this.scheduled_ = false; } MutationObserver.prototype = { @@ -340,6 +340,10 @@ if (node === this.target) return; + // Make sure we remove transient observers at the end of microtask, even + // if we didn't get any change records. + scheduleCallback(this.observer); + this.transientObservedNodes.push(node); var registrations = registrationsTable.get(node); if (!registrations) diff --git a/test/js/MutationObserver/transient.js b/test/js/MutationObserver/transient.js index 1f85cdd..ffc718a 100644 --- a/test/js/MutationObserver/transient.js +++ b/test/js/MutationObserver/transient.js @@ -273,6 +273,34 @@ suite('MutationObserver', function() { var grandChild = child.appendChild(document.createElement('span')); }); + // https://dom.spec.whatwg.org/#notify-mutation-observers + test('removed at end of microtask', function(done) { + var div = document.createElement('div'); + var child = div.appendChild(document.createTextNode('text')); + var observer = new MutationObserver(function() {}); + observer.observe(div, { + characterData: true, + subtree: true + }); + div.removeChild(child); + + records = observer.takeRecords(); + assert.equal(records.length, 0); + + // Run after all other end-of-microtask things, like observers, have + // had their chance to run. Use `setTimeout(4)` to keep the test isolated + // from details of the MutationObserver system and to have it run late + // enough on browsers without true microtasks. + setTimeout(function() { + child.data = 'changed'; + + records = observer.takeRecords(); + assert.equal(records.length, 0); + + done(); + }, 4); + }); + }); }); \ No newline at end of file