Thanks for diving into this Andrew, this is really useful! IIUC you're
saying: When a parent window references an object from a child iframe, the
child iframe's memory compartment cannot be garbage collected.

That seems to make sense.

In the case of Music right now, the sharing of objects is reversed. The
child iframe is handed references to objects in the parent window. I guess
in this case we're a little safer as this *should* allow Gecko to cleanup
the iframe compartment as no objects *should* have leaked out from there.

Once all references to shared objects are freed in parent and child, can
the child memory compartment be destroyed? Or once sharing has happened
does the child iframe compartment have to be kept alive until the parent
dies.

> Is the actual performance problem here just returning to (and waiting
> for) the event loop to continue execution?

Correct. We would ideally use normal .postMessage(), but in practice we
found messages were being pushed right to the end of the task queue and
could take seconds to be sent/received when the task queue was busy with
startup. This bug [1] is tracking that issue, but there doesn't seem to be
any solutions other than: 'Make Gecko do less stuff on main-thread during
startup'; which doesn't sound easy or likely to happen any time soon.

Do you have any suggestions?

[1] https://bugzilla.mozilla.org/show_bug.cgi?id=1164539

*W I L S O N  P A G E*

Front-end Developer
Firefox OS (Gaia)
London Office

Twitter: @wilsonpage
IRC: wilsonpage

On Tue, Nov 17, 2015 at 1:04 AM, Kyle Huey <[email protected]> wrote:

> On Tue, Nov 17, 2015 at 5:22 AM, Andrew Sutherland
> <[email protected]> wrote:
> > At the NGA wrap-up this past Thursday it was mentioned that
> > https://github.com/gaia-components/bridge/issues/69 was landed to deal
> with
> > latency problems with bridge.js versus postMessage and one of the
> > losses/wins was that structured cloning no longer happens.
> >
> > In retrospect, this worried me, so I made this test:
> > https://gist.github.com/asutherland/79d629e2c9d1b7a16a2c and runnable at
> > https://clicky.visophyte.org/tests/iframe-entrainment/parent-root.html
> >
> > The tl;dr is that holding onto any non-value objects provided in a
> message
> > seems to entrain the iframe's window and all of its global/expandos (but
> not
> > the DOM), which is concerning and eliminates many of the intended
> benefits
> > of the use of iframes.
> >
> > == Investigation:
> >
> > In the test:
> > - We have a child iframe.
> > - Script in the child iframe creates a 2 megabyte Uint8Array and saves
> it on
> > its window and a 3 megabyte Uint8Array (that may end up as a 4 meg
> > allocation) and saves it on a DOM node as an expando. This helps us tell
> > what gets entrained.
> > - Script in the child iframe creates an object and posts it to the parent
> > using MessageEvent and dispatchEvent as used by bridge.js
> > - The parent retains a reference to the data posted on its window.
> > - The parent kills off the child iframe.
> >
> > And then I as a tester using firefox nightly:
> > - Use about:memory to force aggressive GC by hitting the "minimize memory
> > usage" button.
> > - "Measure" a "verbose" memory report.
> > - Search for "child-frame.html" and see that the window still exists with
> > its compartment in a detached window at
> > "top(none)/detached/window(
> http://localhost/t-html/iframes/child-frame.html)"
> > and that the compartment includes "2,097,296 B (03.15%) --
> > class(ArrayBuffer)"
> >
> > Testing variations:
> > - If we do not save off the data in the parent by commenting out only the
> > saving line, then the child window is not entrained.  (So there's nothing
> > I'm screwing up in the parent or child otherwise to cause the iframe/its
> > window to be entrained.)
> > - If the child saves a reference to the expando DOM node, then we have 5
> MiB
> > of ArrayBuffer still around.
> > - Creating the data using Object.create(null) to avoid the prototype
> chain
> > has no beneficial effect, the window/global is still entrained.
> > - Trying to just clobber the contents of the iframe via setting the
> `src` to
> > a data URI does not avoid the leak.
> >
> > This suggests that cross-compartment wrapper-cutting does not occur in
> this
> > case (makes sense) and that although the DOM and its expandos are able
> to be
> > collected if there are no window-rooted references, the window global is
> > otherwise kept alive as a static root.
>
> The window global is the parent of the objects you obtained from the
> message, so it will be held alive here. Wrapper cutting doesn't come
> into play because everything involved here is content. If we *did* cut
> wrappers (in other words, if your parent frame here were chrome)
> attempting to access what you obtained from the message would throw
> exceptions.
>
> > Since one of the main goals of the iframes seems to be to make it
> impossible
> > or at least harder for buggy code in the iframes to leak data, this seems
> > concerning.
> >
> > Is this something that we're aware of and are there platform mitigation
> bugs
> > that exist?  (I doubt wrapper-cutting is viable for the web as it exists,
> > but maybe the platform could reap the window global or its expandos?)  It
> > sounded like the apparent performance of the NGA-ported apps really
> depends
> > on the https://github.com/gaia-components/bridge/issues/69 fix to avoid
> > yielding to the busy event loop.  Unfortunately
> > JSON.parse(JSON.stringify(message.data)) in the receiving window is the
> only
> > easy way that jumps out at me to try and "bless" the objects into the
> > current compartment using mechanism available to non-chrome code, but:
> > - That's not viable unless we're throwing away all the fidelity of
> > structured cloning.
> > - That means the GC engine is still on the hook to actually reap those
> > references out of the receiving window before the iframe can be totally
> > GC'ed, we're just upper-bounding their lifetime by avoiding any
> possibility
> > of the bridge.js consumers of holding onto data.
> >
> > But maybe a structured clone polyfill like
> > https://github.com/traviskaufman/cycloneJS could work, noting that I
> haven't
> > audited that it really avoids any references to the source object after
> the
> > fact.
>
> It's impossible to reap the global as long as any objects parented to
> it are held alive.
>
> Is the actual performance problem here just returning to (and waiting
> for) the event loop to continue execution?
>
> - Kyle
> _______________________________________________
> dev-fxos mailing list
> [email protected]
> https://lists.mozilla.org/listinfo/dev-fxos
>
_______________________________________________
dev-fxos mailing list
[email protected]
https://lists.mozilla.org/listinfo/dev-fxos

Reply via email to