-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Element resize listening via <iframe> breaks on destruction #4752
Comments
The proposed fix isn't complete because I'm seeing the component get destroyed after the DOM that contains it is torn down, so it still errors out trying to detach the event listener from a |
Huh. So it seems like svelte tears down the DOM for a component in order from top-to-bottom, including components. It won't add a https://svelte.dev/repl/dde83e1f676443bba7038a5bf0296326?version=3.21.0 d(detaching) {
if (detaching) detach(a);
destroy_component(child);
} That seems wrong and counter-intuitive to me. I'd expect the component to be destroyed before it's containing element like this. d(detaching) {
+ destroy_component(child);
if (detaching) detach(a);
- destroy_component(child);
} If I swap them around my trivial repros/examples work fine, can anyone think of a reason why that would be a terrible idea? Worth noting that I haven't been able to figure out a clean way to do this in the compiler yet, that'll come next I guess. |
I hardcoded our copy of @mrkishi is there a reason we need to support two separate paths here? Could we just use the message-passing version in all cases or is there a downside to that flow that I'm unaware of? |
svelte/src/runtime/internal/dom.ts Lines 257 to 293 in ff5f252
Something kinda like this? if (computed_style.position === 'static') {
node.style.position = 'relative';
}
const iframe = element('iframe');
+ iframe.src = `data:text/html,<script>onresize=function(){parent.postMessage(0,'*')}</script>`;
iframe.setAttribute('style',
`display: block; position: absolute; top: 0; left: 0; width: 100%; height: 100%; ` +
`overflow: hidden; border: 0; opacity: 0; pointer-events: none; z-index: ${z_index};`
);
iframe.setAttribute('aria-hidden', 'true');
iframe.tabIndex = -1;
- let unsubscribe: () => void;
-
- if (is_crossorigin()) {
- iframe.src = `data:text/html,<script>onresize=function(){parent.postMessage(0,'*')}</script>`;
- unsubscribe = listen(window, 'message', (event: MessageEvent) => {
- if (event.source === iframe.contentWindow) fn();
- });
- } else {
- iframe.src = 'about:blank';
- iframe.onload = () => {
- unsubscribe = listen(iframe.contentWindow, 'resize', fn);
- };
- }
-
- append(node, iframe);
+ const unsubscribe = listen(window, 'message', (event: MessageEvent) => {
+ if (event.source === iframe.contentWindow) fn();
+ });
+
+ append(node, iframe);
return () => {
detach(iframe);
- if (unsubscribe) unsubscribe();
+ unsubscribe();
};
} |
The only reason for the check was to do less stuff when possible. At this point, what about just checking for |
I wasn't able to determine conclusively if that would lead to leaking the event handler. My knowledge of when event handlers get leaked is apparently a bit rusty 😕 If you're confident it's safe I'm totally on-board and can have a PR up shortly! |
@@ -11,15 +11,17 @@
`display: block; position: absolute; top: 0; left: 0; width: 100%; height: 100%; ` +
`overflow: hidden; border: 0; opacity: 0; pointer-events: none; z-index: ${z_index};`
);
iframe.setAttribute('aria-hidden', 'true');
iframe.tabIndex = -1;
+ const crossorigin = is_crossorigin();
+
let unsubscribe: () => void;
- if (is_crossorigin()) {
+ if (crossorigin) {
iframe.src = `data:text/html,<script>onresize=function(){parent.postMessage(0,'*')}</script>`;
unsubscribe = listen(window, 'message', (event: MessageEvent) => {
if (event.source === iframe.contentWindow) fn();
});
} else {
iframe.src = 'about:blank';
@@ -28,10 +30,15 @@
};
}
append(node, iframe);
return () => {
- if (unsubscribe) unsubscribe();
+ if(crossorigin) {
+ unsubscribe();
+ } else if(unsubscribe && iframe.contentWindow) {
+ unsubscribe()
+ }
+
detach(iframe);
};
} Seem reasonable @mrkishi? Dunno if that's gonna run afoul of TS, I've only tried it in the generated |
Fixes sveltejs#4752 by not attempting to call .removeEventListener if the iframe.contentWindow no longer exists.
Tried it, passes all the svelte tests so I assume TS is ok with it 😅 |
Fixes sveltejs#4752 by not attempting to call .removeEventListener if the iframe.contentWindow no longer exists.
Fixes #4752 by not attempting to call .removeEventListener if the iframe.contentWindow no longer exists.
Your workaround has been released in 3.23.0, thank you! |
Fixes sveltejs#4752 by not attempting to call .removeEventListener if the iframe.contentWindow no longer exists.
Describe the bug
We run in an embedded browser that uses an older version of WebKit and have run into a bug in 3.21.0, likely due to #2989 which fixed #2147. When destroying elements that use
bind:width
,bind:height
, etc we get a JS error in the returned unsubscription function fromlisten()
.svelte/src/runtime/internal/dom.ts
Lines 60 to 63 in b336442
It can't call
node.removeEventListener
because theiframe.contentWindow
used to set up the listener previously has already been set tonull
by the<iframe>
being detached from the DOM.svelte/src/runtime/internal/dom.ts
Line 283 in b336442
Logs
To Reproduce
Running
hide()
will trigger the error, but obviously only in older versions of webkit like the one we useExpected behavior
The element would clean itself up and hide without errors.
Information about your Svelte project:
Severity
This blocks us from using 3.21.0, which we need to fix all of the outro issues.
Additional context
There's a relatively small fix which requires shuffling around the detachment/unsubscription order a bit.
<iframe>
before the destruction of its parentsvelte/src/compiler/compile/render_dom/wrappers/Element/index.ts
Lines 588 to 590 in b336442
<iframe>
to before it is detached from the DOM.svelte/src/runtime/internal/dom.ts
Lines 289 to 292 in b336442
Since they originally authored #2989 I'm gonna ping @mrkishi here to see if they've got any ideas on the safety of these proposed changes. I'll start getting a PR together shortly.
The text was updated successfully, but these errors were encountered: