Skip to content
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

Switches URL polyfill to whatwg-url #59

Merged
merged 1 commit into from
Jul 21, 2024
Merged

Switches URL polyfill to whatwg-url #59

merged 1 commit into from
Jul 21, 2024

Conversation

mhassan1
Copy link
Collaborator

@mhassan1 mhassan1 commented Jul 11, 2024

This PR replaces the URL polyfill with whatwg-url. It uses browserify to bundle and transpile whatwg-url.

Resolves #53.

@mhassan1 mhassan1 marked this pull request as draft July 12, 2024 02:52
mhassan1 added a commit that referenced this pull request Jul 16, 2024
…`ArrayBuffer` polyfills (#62)

This PR fixes a bunch of issues with configuration and detection of
polyfills required for an improved `URL` polyfill
(#59):
* adds tests that iterators are also iterable
* updates browser configs where iterator behaviors differ with and
without the `Symbol` polyfill
* adds detection for Firefox <44 legacy iterators
* adds detection for non-compliant `TextEncoder`
* updates browser configs and detection for non-compliant `ArrayBuffer`
@mhassan1 mhassan1 marked this pull request as ready for review July 16, 2024 13:04
@mhassan1
Copy link
Collaborator Author

@romainmenke This is ready for review.

Copy link
Member

@romainmenke romainmenke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some notes and questions, overal looks good!

The polyfill is indeed quite large, but I think that going for correctness is the right choice here.

polyfills/URL/_entry.js Outdated Show resolved Hide resolved
polyfills/URL/_entry.js Outdated Show resolved Hide resolved
polyfills/URL/config.toml Show resolved Hide resolved
polyfills/URL/_webidl-conversions.js Outdated Show resolved Hide resolved
@mhassan1 mhassan1 force-pushed the fix-url branch 2 times, most recently from 9e25e15 to f7149d0 Compare July 18, 2024 18:53
@mhassan1
Copy link
Collaborator Author

@romainmenke I've changed the way update.task.js works and force-pushed. Please take a look.

@mhassan1
Copy link
Collaborator Author

This PR Is blocked by #64.

Copy link
Member

@romainmenke romainmenke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Thank you @mhassan1 for finding an alternative here 🙇

@mhassan1 mhassan1 merged commit c2a4d0b into main Jul 21, 2024
28 checks passed
@mhassan1 mhassan1 deleted the fix-url branch July 21, 2024 20:15
@romainmenke
Copy link
Member

🤔 CI seems to be more flaky now in very old browser versions.
I suspect that the size is causing issues. (network or memory?)

Is there anything you can think of we can improve here?

@mhassan1
Copy link
Collaborator Author

🤔 CI seems to be more flaky now in very old browser versions. I suspect that the size is causing issues. (network or memory?)

Is there anything you can think of we can improve here?

Flaky on URL tests? I see timeouts are being hit here. Should be just increase the timeouts for the URL tests?

@mhassan1
Copy link
Collaborator Author

mhassan1 commented Jul 22, 2024

For reference, here are the largest polyfills we have:

840K	polyfills/Intl/DateTimeFormat/~timeZone/all/polyfill.js
532K	polyfills/Intl/DateTimeFormat/~timeZone/golden/polyfill.js
344K	polyfills/URL/polyfill.js
148K	polyfills/Intl/Locale/polyfill.js
144K	polyfills/String/prototype/normalize/polyfill.js

So URL is not the biggest one we have. Maybe there's something else about it that makes it slow to execute.

@romainmenke
Copy link
Member

I think that maybe it is a really really slow parser.

Probably a combination of using many other features (e.g. TextEncoder, Symbol, iterators, Set, ...) and not having even the most basic optimizations (e.g. cp2 === p(":") vs. cp2 === 58).

I am concerned that url parsing is something that can happen fairly often and frequently in some client side applications. If parsing a single URL can take anywhere between ±100ms and over 2 seconds then I am sure we will be breaking some things downstream.

What options do we have here?

@mhassan1
Copy link
Collaborator Author

mhassan1 commented Jul 24, 2024

I think that maybe it is a really really slow parser.

I ran the URL tests locally with all detect.js files as false (to force all polyfills to load in my modern browser). The test suite ran in 0.08s. Of course, my laptop is much more powerful than the BrowserStack machines; maybe this polyfill performs fine on moderately powerful machines running old browser versions.

I just ran that same test (all detect.js files as false and all config.toml as *) against a bunch of old Chrome versions on Windows 11. It seems like the really old Chrome versions (less than 46) are 10x slower than the others:

Windows 11
----------
Chrome 50: 1.40s
Chrome 49: 1.28s
Chrome 48: 1.84s
Chrome 47: 1.67s
Chrome 46: 1.73s
Chrome 45: 11.48s
Chrome 44: 11.54s
Chrome 43: 11.91s
Chrome 42: 11.96s
Chrome 41: 13.36s
Chrome 40: 16.67s

Windows XP
----------
Chrome 46: 1.23s
Chrome 45: 11.39s

I've narrowed down the issue to Object.defineProperty with get/set, which is much slower in Chrome 45 and below. Compare for (var i = 0; i < 5000; i++) Object.defineProperty({},0,{get:function(){return 0}}) between Chrome 45 (~4s) and 46 (instant). So, I don't think the URL parser itself is slow, but it appears slow because it allocates many ArrayBuffer instances, which call Object.defineProperty many times (link).

Chrome 45 doesn't get the ArrayBuffer polyfill, which is why it's fast when running the URL tests normally (without forcing it to get the polyfill), but Chrome 44 and below need the ArrayBuffer polyfill.

Options:

  1. Increase the timeout on the URL tests for Chrome 44 and below (because they get the ArrayBuffer polyfill)
  2. Drop support for Chrome 44 and below (0.03% usage total, according to https://caniuse.com/usage-table)
  3. Refactor the ArrayBuffer polyfill to not use Object.defineProperty in a loop

As a test of option 3, I replaced repeated Object.defineProperty calls in the ArrayBuffer loop with Object.defineProperties, but it made no improvement.

@romainmenke
Copy link
Member

Thank you for investigating further @mhassan1 🙇
And sorry for the delay in picking this thread back up.

At this time I think we might need to revert the changes to URL.
I don't think we can drop support for the affected browser versions and I prefer dropping support for specific versions from a position we can revert from without it being a breaking change. (having to also remove shipped features like URL.canParse would also be breaking)

I think that the value of a working URL polyfill for these very old browser versions is higher than the value of URL.parse and URL.canParse in any browser version.

Do you see a path forwards that allows us to have both this polyfill for URL and preserve support for these old browser versions?

@romainmenke
Copy link
Member

Upon closer inspection I am more convinced that we should revert this change.
The URL polyfill is currently only used for also some very old browser versions and the number of versions that could be served the updated whatwg polyfill and still actually need it is even smaller still.

Only:

  • Chrome 45-60
  • Safari 10-11
  • ...

I think it is fine to have a URL.parse and URL.canParse polyfill that only work fully as expected in browsers with a native URL implementation that throws in all expected cases.

romainmenke added a commit that referenced this pull request Sep 23, 2024
romainmenke added a commit that referenced this pull request Sep 23, 2024
@romainmenke
Copy link
Member

I've opened a PR to revert this change here: #72

But waiting to hear your thoughts and concerns first :)
I really appreciate the time and effort you've put into this polyfill and maybe you see path forwards that I am currently overlooking.

@mhassan1
Copy link
Collaborator Author

Just to clarify: the old URL polyfill had incorrect behavior, regardless of URL.parse, so I don't think it would be better to go back to it. I think correct behavior is more important than performance.

The performance issue is caused by the frequent use of the ArrayBuffer polyfill, which seems to be slow. Maybe we could make it faster.

Or, we could have two URL polyfills that load dynamically based on the browser, but that would still mean that we are serving an incorrect polyfill to old browsers.

@mhassan1
Copy link
Collaborator Author

I've put up a PR for another proposal: remove the need for the ArrayBuffer polyfill for some browsers by removing the need for TypedArray.prototype.slice and TypedArray.prototype.indexOf: #73.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

URL polyfill does not throw for invalid input
2 participants