This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 22759 - It is not clear what happens to the existing transient observers when MutationObserver.observe is called
Summary: It is not clear what happens to the existing transient observers when Mutatio...
Status: RESOLVED FIXED
Alias: None
Product: WebAppsWG
Classification: Unclassified
Component: DOM (show other bugs)
Version: unspecified
Hardware: PC Linux
: P2 normal
Target Milestone: ---
Assignee: Anne
QA Contact: public-webapps-bugzilla
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2013-07-22 20:40 UTC by Olli Pettay
Modified: 2013-08-07 09:39 UTC (History)
5 users (show)

See Also:


Attachments

Description Olli Pettay 2013-07-22 20:40:43 UTC
"2. If target's list of registered observers already includes a registered
observer associated with the context object, replace that registered
observer's options with options. " doesn't explain that transient observers
should be removed.
Comment 1 Rafael Weinstein 2013-07-22 23:48:05 UTC
Blink follows gecko in that when observe() replaces an earlier observation, the connected transient observers are cleared.
Comment 2 Anne 2013-07-23 01:30:43 UTC
  <li><p>If <var title="">target</var>'s list of
- <a href="#registered-observer" title="registered observer">registered observers</a> already
- includes a <a href="#registered-observer">registered observer</a> associated with
- the <a href="#context-object">context object</a>, replace that
- <a href="#registered-observer">registered observer</a>'s <b>options</b> with <var title="">options</var>.
+ <a href="#registered-observer" title="registered observer">registered observers</a> already includes a
+ <a href="#registered-observer">registered observer</a> associated with the <a href="#context-object">context object</a>, replace
+ that <a href="#registered-observer">registered observer</a>'s <b>options</b> with <var title="">options</var> and
+ remove any
+ <a href="#transient-registered-observer" title="transient registered observer">transient registered observers</a> whose
+ <b>observer</b> is the <a href="#context-object">context object</a>.

Is the diff I have. Okay?
Comment 3 Olli Pettay 2013-07-23 21:39:47 UTC
Looks ok, assuming 'transient registered observers' there means the transient
observers related to the MutationObserver which observe() is being called, not
all transient observers (in case there are many MutationObservers).
Comment 5 Travis Leithead [MSFT] 2013-07-23 23:47:30 UTC
Thanks everyone.
Comment 6 Travis Leithead [MSFT] 2013-07-24 00:02:17 UTC
One more thing: what about the case where one mutation observer is observing two or more nodes with different options? Current implementations are very precise and only remove the transient registered observers belonging to a single mutation observer object that also were created exclusively for the options pertaining to a certain node. This is better illustrated by example:

<!doctype html>
<div id="div">
  <div id="gone">
    <div id="subgone"></div>
  </div>
</div>
<script>
onload = function() {
 var mutationObserver = new MutationObserver(function checkpoint(list) {
    alert(list.length);
 });
 mutationObserver.observe(document.body, 
                          { subtree: true, childList: true});
 mutationObserver.observe(document.body.firstElementChild, 
                          { subtree: true, attributes: true});
 // Cause a removal of "subgone" to create two transient observers, 
 // one for attibutes and one for childList
 var subgone = document.getElementById("div").removeChild(document.getElementById("gone"));
 // Re-observe the attributes observer (same options--to reset 
 // it and its transients)
 mutationObserver.observe(document.body.firstElementChild, 
                          { subtree: true, attributes: true});
 // Did this clear both transient observers on "subgone" -- find out:
 // It certainly must have cleared the "attributes" transient observer
 // Did it also clear the childList observer?
 subgone.appendChild(document.createElement("span"));
 // Add an attribute: expected that it _doesn't_ get recorded.
 subgone.setAttribute("mytest", "test");
}
</script>

Latest Firefox/Chrome report only two records, not 3, so even on a single mutation observer, not all of its transient observers are cleared out on re-observe--just those related to the node on which the observe method is called.

Just want to make sure this is super-clear in the spec :-)
Comment 7 Anne 2013-07-24 05:40:23 UTC
So I guess what we want:

For each registered observer in target's list of registered observers whose observer is the context object:

1. Remove it if it's a transient registered observer.
2. Replace its options with /options/.
Comment 9 Travis Leithead [MSFT] 2013-07-29 18:34:03 UTC
The fix does not quite match what implementations are doing. Here's what I think is further required:

In the "To remove a node from a parent, optionally with suppress observers flag set, run these steps:" algorithm, step 7 says to create a transient registered observer and append it to node's list of registered observer. We need to additionally be able to track this transient registered observer's relationship to the original node so that when observe() is called again with the original target and options, this "forked" registered observer can also be removed (which it is not according to the current algorithm that only iterates the target's list of registered observers.

This can be accomplished a number of ways, for example, adding a list of child transient observers for each registered observer (and traversing it at the appropriate time). However, the simplest way to do this re-using existing lists and with minimal changes to the spec seems to be the following:

1. In step 7 of the above-mentioned algorithm, the node newly associated with the transient registered observer needs to be additionally added to the context object's list of nodes.

2. Step 2 of "The observe(target, options) method must run these steps:" algorithm, must be expanded. Instead of iterating target's list of registered observers, it must actually iterate the context object's list of nodes, and then for each node iterate that node's list of registered observers looking for transient registered observers whose "observer" matches the context object and removing it. Then it has to explicitly perform current step 2.2 on just the target's list of registered observers (or you would end up resetting other nodes you legitimately independently registered on).

Bonus:
Additionally, step 2.3 of the "To invoke MutationObserver objects, run these steps:" algorithm can be made more explicit--in that it traverse's mo's list of nodes, and for each node in said list, it searches the node's list of registered observers for those that refer to mo and removes them all.
Comment 10 Rafael Weinstein 2013-07-30 21:28:19 UTC
I like the first idea: ... "adding a list of child transient observers for each registered observer (and traversing it at the appropriate time)."

Not only is it clear what's going on, it also happens to be exactly what Blink/Webkit do (I'm guess Gecko and Trident are similar).
Comment 11 Anne 2013-07-31 18:00:21 UTC
I have to admit I'm kinda lost at this point. If the "list of child transient observers" more closely matches implementations I'd be happy with adding that, but I need some guidance.
Comment 12 Rafael Weinstein 2013-07-31 22:05:22 UTC
The idea is that any given transient registration is associated a given "source" observer registration. In the case we are discussing, when the options of that registration are removed, then the transient registrations associated with it must be removed.

It's similar to the step upon delivery: "Remove all transient registered observers whose observer is mo.", but that removes all transient registrations associated with observer. In this case we need to remove only those associated with a given registration *of that observer*.
Comment 13 Olli Pettay 2013-07-31 22:27:12 UTC
(In reply to comment #12)
> The idea is that any given transient registration is associated a given
> "source" observer registration. In the case we are discussing, when the
> options of that registration are removed, then the transient registrations
> associated with it must be removed.
Well, isn't that what https://www.w3.org/Bugs/Public/show_bug.cgi?id=22759#c7 says, step 1.
Comment 14 Rafael Weinstein 2013-07-31 22:30:22 UTC
Maybe I'm confused, but that line suggests to me that it will only remove the transient registrations currently registered on that node.

Am I reading it wrong?

What we need to do is to remove all transient registrations on *any* node which are associated with the registration which is being "reset" (having it's options changed).
Comment 15 Olli Pettay 2013-07-31 22:31:28 UTC
Ah, indeed. I was reading Anne's comment wrong.
Comment 16 Anne 2013-08-01 13:52:53 UTC
Alright, I feel like we're getting closer. I added the source concept. I wish I could be more confident about this stuff.
Comment 17 Rafael Weinstein 2013-08-06 20:10:00 UTC
It looks right to me now. Travis?
Comment 18 Travis Leithead [MSFT] 2013-08-06 22:11:52 UTC
Yep, works for me too. Thanks Anne.
Comment 19 Anne 2013-08-07 09:39:57 UTC
https://github.com/whatwg/dom/commit/c1ca11724f299813baff976c19f31f55ddc7fb3c was that last commit.

Thanks for the multiple rounds of review and Microsoft for finding this bug in the first place!