Bug 107859

Summary: adoptingNode should update its prototype chain
Product: WebKit Reporter: Ryosuke Niwa <rniwa>
Component: DOMAssignee: Nobody <webkit-unassigned>
Severity: Normal CC: annevk, ap, arv, ggaren, mike, ojan, sam, syoichi
Priority: P2    
Version: 528+ (Nightly build)   
Hardware: Unspecified   
OS: Unspecified   

Description Ryosuke Niwa 2013-01-24 13:51:18 PST
Apparently WebKit doesn't update the prototype chain when a node is adopted:

We need to fix that.
Comment 1 Geoffrey Garen 2013-01-24 14:23:39 PST
Is there a straw man proposal for a standard behavior here?

I emailed some concerns to the w3 list, but they don't seem to have posted. I'll post them again here.

Hi folks.

Responding to a few things out of order:

> If it stays the old proto, then you'll have an element in the new 
> document that tests false for instanceof against things in that window, no?

FWIW, I don't buy the instanceof argument.
	(a) Passing any object between windows breaks instanceof. I'm not convinced of the value of working around that in one special case.
	(b) Once you change from Window A's prototype to Window B's, instanceof in Window B will work but instanceof in Window A will now be broken.
	(c) node.onclick instanceof Function, and instanceof any custom properties, will still fail.

> 2)  If a script adopts a node from a subframe and then unloads the 
> subframe or removes it from the DOM, remembering the proto means you 
> leak the entire global object _and_ the entire DOM of the subframe

Changing the prototype doesn't resolve this issue.

Any event listener on the node will keep the entire global object and DOM alive, through the scope chain's link to the lexical environment and its .document property.

Also, any custom properties on the node wrapper may point to the old lexical environment, directly or indirectly.

Fundamentally, the DOM is one giant memory leak.

> Well... Should the proto remain the proto associated with the window of 
> the old document, or should it become the proto associated with the 
> window of the new document?

In a way, I think this is asking the wrong question. Our task isn't to pick a prototype -- it's to pick a set of observable properties. In a prototype-based language, the prototype is just one way to associate some properties with an object. 


{ y: 0 }
{ x: 0 }
{  }

is logically equivalent to this:

{ x: 0, y: 0 }

If we want to logically sever the node's relationship to its old environment, and give it a new relationship to a new environment, we should be asking about all of its properties, and not just the ones belonging to its prototypes.

If that's the goal, I think the best solution is to create a second wrapper. Direct references to the old wrapper would continue to see that wrapper's identity and custom properties (including those implemented by its prototypes), but new references returned by the DOM (e.g. via getElementById) would use a new wrapper, with a new identity and a new set of custom properties defined by its new prototypes.

You might think that it's strange for a node's identity to change when it is adopted. But I think that's the most honest way to represent the operation you're describing. If adopting a node changes its very type, it's probably right to say that it's no longer equal to what it used to be.

Comment 2 Anne van Kesteren 2016-08-19 05:27:24 PDT
Ryosuke, on IRC today you mentioned this was resolved in WebKit by keeping the prototype chain around of whatever global created the element. Should this bug be closed?
Comment 3 Ryosuke Niwa 2016-08-19 13:56:45 PDT
The DOM/HTML spec currently agrees with WebKit’s behavior, and we no longer GC JS wrappers for DOM objects. So we have a consistent behavior of using the prototype object in the original global at this point.

The only remaining bug I’m aware of is https://bugs.webkit.org/show_bug.cgi?id=160952.