-
Notifications
You must be signed in to change notification settings - Fork 27k
fix(common): canceled JSONP requests won't throw error with missing callback function #36807
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed this line because the handler should be removed by the handler itself:
angular/packages/common/http/src/jsonp.ts
Line 107 in 2365bb8
| delete this.callbackMap[callback]; |
JoostK
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It appears to be possible to prevent the script from executing by adopting the script element into another document, as described in https://html.spec.whatwg.org/multipage/scripting.html#execute-the-script-block. So instead of removing the script element as we do now, it could instead be adopted into a dummy document. I think that doing so is beneficial in terms of resources, as it avoids the need to parse and execute the script.
|
@martinsik - would you have time to address the comments from @JoostK? |
656fa16 to
20c4c8a
Compare
|
It's been so long I've completelly forgot I ever made this PR :). @JoostK I made a small demo to test if changing <script>'s document will prevent it from exeuction and it does work https://stackblitz.com/edit/js-gs6nph?file=index.js I'm not sure why |
JoostK
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your continued effort on this PR. It looks good to me; I'll request a review from @alxhub as code owner of fw-http so we can get this landed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this sufficient to verify that the callback handler wasn't executed? Isn't this check only indicating that cleanup happened?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated the test to also check that the callback wasn't invoked.
|
Btw don't worry about the Saucelabs failures; they have been very flaky over the last couple of weeks and #44281 should help make it more stable. |
920e501 to
47d1168
Compare
|
@JoostK I updated this PR and implemented your comments. |
packages/common/http/src/jsonp.ts
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering if it'd be better to create a document lazily as it's not always needed?
Something like this:
if (!finished) {
// Issue #34818
// Changing <script>'s ownerDocument will prevent it from execution.
// https://html.spec.whatwg.org/multipage/scripting.html#execute-the-script-block
if (!this.dummyDocument) {
this.dummyDocument = this.document.implementation.createHTMLDocument();
}
this.dummyDocument.adoptNode(node);
}
Could you please also add a comment above the dummyDocument field to explain why it's needed?
I was also thinking if we can rename the field. May be something like foreignDocument?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably it'd also make sense to move that logic to a separate function, so it acts as an extra documentation as well:
// ... add docs here ...
private removeListeners(node: HTMLElement): void {
// Issue #34818
// Changing <script>'s ownerDocument will prevent it from execution.
// https://html.spec.whatwg.org/multipage/scripting.html#execute-the-script-block
if (!this.dummyDocument) {
this.dummyDocument = this.document.implementation.createHTMLDocument();
}
this.dummyDocument.adoptNode(node);
}
and after that:
if (!finished) {
this.removeListeners(node);
}
The benefit is that the main code path is straightforward (we just remove listeners) and the complexity is in the removeListeners function itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AndrewKushnir I moved it into a separate method. I'm not sure if removeListeners isn't confusing because it's not really removing any listeners.
47d1168 to
91ca2ed
Compare
|
@AndrewKushnir I rebased the code and implemented your suggestions. |
AndrewKushnir
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@martinsik the change looks good 👍
I'd like to ask @JoostK to take another look. After that we can run the necessary tests in Google's codebase and proceed with next steps if everything looks good.
Thank you.
packages/common/http/src/jsonp.ts
Outdated
| * When a pending <script> is unsubscribed we'll move it to this document, so it won't be | ||
| * executed. | ||
| */ | ||
| private foreignDocument!: Document; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I'd personally move this to become a top-level variable, as this can be safely shared between multiple instances (although unlikely to be relevant except for tests) and it minimizes better.
I'd also request not to use ! here, as we can easily include |undefined in the type to make reads of this field null-safe. Or use Document|null = null; which produces more code but has the benefit that reads from this class stay monomorphic. But that wouldn't be a concern if it's a top-level var (nor is this performance sensitive code anyway, I'm mentioning it as a general takeaway).
tldr; I'd declare let foreignDocument: Document|undefined; as top-level declaration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JoostK I moved foreignDocument outside of the class, but it seems like I'll need to be using ! in foreignDocument!.adoptNode(script); because this.document is of type any so TypeScript can't know what type is returned from createHTMLDocument() .
I could use typecasting on this (this.document.implementation as DOMImplementation).createHTMLDocument() but that's probably not correct because document could use a different implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, interesting. If we're relying on createHTMLDocument to be available, then we might as well do the cast. If we don't want to rely on that then we'd need to account for the situation where implementaiton or createHTMLDocument is not available as we'd expect, but I don't think there's a reasonable alternative we could use in that case so that seems not viable to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I added typecast to DOMImplementation.
6da6cc6 to
aa9a76d
Compare
|
@martinsik thanks, LGTM. Could you please rebase on latest master to get CI to run? |
…issing callback function This commit fixes a use-case where unsubscribing from a JSONP request will result in "Uncaught ReferenceError: ng_jsonp_callback_xy is not defined" thrown into console. Unsubscribing won't remove its associated callback function because the requested script will finish loading anyway and will try to call the handler. PR Close angular#34818
handler Cancel pending json handler by adopting its <script> element into another document (https://html.spec.whatwg.org/multipage/scripting.html#execute-the-script-block) This way the browser will prevent the script from being parsed and executed. Fixes angular#34818
aa9a76d to
78aaf76
Compare
|
This PR was merged into the repository by commit 909b21a. |
…6807) handler Cancel pending json handler by adopting its <script> element into another document (https://html.spec.whatwg.org/multipage/scripting.html#execute-the-script-block) This way the browser will prevent the script from being parsed and executed. Fixes #34818 PR Close #36807
…issing callback function (#36807) This commit fixes a use-case where unsubscribing from a JSONP request will result in "Uncaught ReferenceError: ng_jsonp_callback_xy is not defined" thrown into console. Unsubscribing won't remove its associated callback function because the requested script will finish loading anyway and will try to call the handler. PR Close #34818 PR Close #36807
…6807) handler Cancel pending json handler by adopting its <script> element into another document (https://html.spec.whatwg.org/multipage/scripting.html#execute-the-script-block) This way the browser will prevent the script from being parsed and executed. Fixes #34818 PR Close #36807
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [@angular/animations](https://github.com/angular/angular) | dependencies | patch | [`13.2.4` -> `13.2.5`](https://renovatebot.com/diffs/npm/@angular%2fanimations/13.2.4/13.2.5) | | [@angular/common](https://github.com/angular/angular) | dependencies | patch | [`13.2.4` -> `13.2.5`](https://renovatebot.com/diffs/npm/@angular%2fcommon/13.2.4/13.2.5) | | [@angular/compiler](https://github.com/angular/angular) | dependencies | patch | [`13.2.4` -> `13.2.5`](https://renovatebot.com/diffs/npm/@angular%2fcompiler/13.2.4/13.2.5) | | [@angular/compiler-cli](https://github.com/angular/angular) | devDependencies | patch | [`13.2.4` -> `13.2.5`](https://renovatebot.com/diffs/npm/@angular%2fcompiler-cli/13.2.4/13.2.5) | | [@angular/core](https://github.com/angular/angular) | dependencies | patch | [`13.2.4` -> `13.2.5`](https://renovatebot.com/diffs/npm/@angular%2fcore/13.2.4/13.2.5) | | [@angular/forms](https://github.com/angular/angular) | dependencies | patch | [`13.2.4` -> `13.2.5`](https://renovatebot.com/diffs/npm/@angular%2fforms/13.2.4/13.2.5) | | [@angular/platform-browser](https://github.com/angular/angular) | dependencies | patch | [`13.2.4` -> `13.2.5`](https://renovatebot.com/diffs/npm/@angular%2fplatform-browser/13.2.4/13.2.5) | | [@angular/platform-browser-dynamic](https://github.com/angular/angular) | dependencies | patch | [`13.2.4` -> `13.2.5`](https://renovatebot.com/diffs/npm/@angular%2fplatform-browser-dynamic/13.2.4/13.2.5) | | [@angular/router](https://github.com/angular/angular) | dependencies | patch | [`13.2.4` -> `13.2.5`](https://renovatebot.com/diffs/npm/@angular%2frouter/13.2.4/13.2.5) | | [zone.js](https://github.com/angular/angular) ([changelog](https://github.com/angular/angular/blob/master/packages/zone.js/CHANGELOG.md)) | dependencies | patch | [`0.11.4` -> `0.11.5`](https://renovatebot.com/diffs/npm/zone.js/0.11.4/0.11.5) | --- ### Release Notes <details> <summary>angular/angular (@​angular/animations)</summary> ### [`v13.2.5`](https://github.com/angular/angular/blob/HEAD/CHANGELOG.md#​1325-2022-03-02) [Compare Source](angular/angular@13.2.4...13.2.5) ##### animations | Commit | Type | Description | | -- | -- | -- | | [6c61d20476](angular/angular@6c61d20) | fix | allow animations with unsupported CSS properties ([#​45185](angular/angular#45185)) | ##### common | Commit | Type | Description | | -- | -- | -- | | [64da1daa78](angular/angular@64da1da) | fix | canceled JSONP requests won't throw console error with missing callback function ([#​36807](angular/angular#36807)) | | [56ca7d385b](angular/angular@56ca7d3) | perf | make `NgLocalization` token tree-shakable ([#​45118](angular/angular#45118)) ([#​45226](angular/angular#45226)) | ##### compiler-cli | Commit | Type | Description | | -- | -- | -- | | [6c906a5bb9](angular/angular@6c906a5) | fix | Support resolve animation name from the DTS ([#​45169](angular/angular#45169)) | ##### core | Commit | Type | Description | | -- | -- | -- | | [e8fd452bd2](angular/angular@e8fd452) | fix | remove individual commands for updating gold files ([#​45198](angular/angular#45198)) | | [82d772857c](angular/angular@82d7728) | perf | make `Compiler`, `ApplicationRef` and `ApplicationInitStatus` tree-shakable ([#​45102](angular/angular#45102)) ([#​45222](angular/angular#45222)) | | [71ff12c1cc](angular/angular@71ff12c) | perf | make `LOCALE_ID` and other tokens from `ApplicationModule` tree-shakable ([#​45102](angular/angular#45102)) ([#​45222](angular/angular#45222)) | ##### localize | Commit | Type | Description | | -- | -- | -- | | [d388522745](angular/angular@d388522) | fix | avoid imports into `compiler-cli` package ([#​45180](angular/angular#45180)) | #### Special Thanks Andrew Kushnir, Andrew Scott, Charles Lyding, Guillaume Bonnet, Jessica Janiuk, JoostK, Martin Sikora, Paul Gschwendtner, Theodore Brown, dario-piotrowicz and ivanwonder <!-- CHANGELOG SPLIT MARKER --> </details> <details> <summary>angular/angular (zone.js)</summary> ### [`v0.11.5`](https://github.com/angular/angular/blob/HEAD/packages/zone.js/CHANGELOG.md#​0115-httpsgithubcomangularangularcomparezonejs-0114zonejs-0115-2022-03-03) [Compare Source](angular/angular@zone.js-0.11.4...zone.js-0.11.5) ##### Bug Fixes - **zone.js:** async-test should only call done once ([#​45025](angular/angular#45025)) ([dea7234](angular/angular@dea7234)) - **zone.js:** defineProperties should also set symbol props ([#​45098](angular/angular#45098)) ([b437d12](angular/angular@b437d12)), closes [#​44095](angular/angular#44095) - **zone.js:** fix several test cases which trigger `done()` multiple times ([#​45025](angular/angular#45025)) ([d5565cc](angular/angular@d5565cc)) - **zone.js:** only one listener should also re-throw an error correctly ([#​41868](angular/angular#41868)) ([299f92c](angular/angular@299f92c)), closes [#​41867](angular/angular#41867) [/github.com/angular/angular/pull/41562#issuecomment-822696973](https://github.com//github.com/angular/angular/pull/41562/issues/issuecomment-822696973) - **zone.js:** patch global instead of Mocha object ([#​45047](angular/angular#45047)) ([8efbdb5](angular/angular@8efbdb5)), closes [#​42834](angular/angular#42834) - **zone.js:** should continue to executue listeners when throw error ([#​41562](angular/angular#41562)) ([008eaf3](angular/angular@008eaf3)), closes [#​41522](angular/angular#41522) - **zone.js:** update several flaky cases ([#​41526](angular/angular#41526)) ([25a83eb](angular/angular@25a83eb)), closes [#​41434](angular/angular#41434) ##### Features - **zone.js:** add Promise.any() implementation ([#​45064](angular/angular#45064)) ([4d494d2](angular/angular@4d494d2)), closes [#​44393](angular/angular#44393) - **zone.js:** update electron patch to support electron/remote 14 ([#​45073](angular/angular#45073)) ([d65706a](angular/angular@d65706a)), closes [#​43346](angular/angular#43346) </details> --- ### Configuration 📅 **Schedule**: At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, click this checkbox. --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: cabr2-bot <[email protected]> Reviewed-on: https://codeberg.org/Calciumdibromid/CaBr2/pulls/1197 Reviewed-by: Epsilon_02 <[email protected]> Co-authored-by: Calciumdibromid Bot <[email protected]> Co-committed-by: Calciumdibromid Bot <[email protected]>
|
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
Fixes #34818
Unsubscribing from a JSONP request will throw console errors such as
Uncaught ReferenceError: ng_jsonp_callback_41 is not defined. This is happening because the callback function is removed right on unsubscription but this doesn't stop the JSONP script from loading.https://stackblitz.com/edit/angular-4zmfjr?file=src/app/app.component.ts
What is the new behavior?
Unsubscribing from JSONP request won't remove callback handler. Handler is then removed by the handler itself:
angular/packages/common/http/src/jsonp.ts
Line 107 in 2365bb8
Does this PR introduce a breaking change?