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

iOS 9.3 limits the number of times pushState can be called #291

Closed
iatzmon opened this issue May 10, 2016 · 15 comments
Closed

iOS 9.3 limits the number of times pushState can be called #291

iatzmon opened this issue May 10, 2016 · 15 comments
Labels

Comments

@iatzmon
Copy link

iatzmon commented May 10, 2016

Hi,
Since version 9.3 of iOS Apple has limited the number of times a page can call the pushState API to 100 times. Any further attempt causes the following exception to be thrown:

SecurityError: DOM Exception 18: An attempt was made to break through the security policy of the user agent.

You can read more about it here.

It seems that this is their (terrible) solution for an exploit using this API.
My app has many pages and can easily reach more than a 100 router transitions. Currently, the only workaround I can think is to catch this exception and reload the application once it happens with the requested route, which resets the pushState counter back to 0.

@taion
Copy link
Contributor

taion commented May 10, 2016

This seems like it might be a pretty valuable workaround to add.

How specific is this exception message?

@Dattaya
Copy link

Dattaya commented May 15, 2016

According to a comment in this bug report: https://bugs.webkit.org/show_bug.cgi?id=156115 it's 100 calls per 30 second interval, so it's not that bad as I thought initially.

@amarchen
Copy link

Depends on how your app works. I was updating URL on every slider move and hit security exception after few seconds of playing with a slider.
Ember.js folks also experienced this issue and reload the page when this exception happens - http://jdurand.com/blog/2016/05/03/ember-history-pushstate-dom-exception-18/

If the critical point is indeed 100 calls per 30 seconds, could the [optional] throttling be a solution? Say, if the code is running on a platform affected (i.e. Mobile Safari), we could either hard limit speed of url update to 3.4 per second.

Or a more clever exponential formula could be possible, so that first updates could happen instantly, then nearly instantly and if code continues pushing state too quickly, speed of updates would go down so that it never goes over 100 per last 30 sec.

@taion
Copy link
Contributor

taion commented May 16, 2016

I'd go with the hard refresh on this exception instead of trying to be clever. I think any attempted cleverness is more likely to cause weird bugs.

Though hitting the history API >3x per second for 30 seconds seems... excessive? For most use cases.

@amarchen
Copy link

I guess throttling (both stupid 3 times a sec and clever with changing number of skipped requests as we come closer to the limit) could be done through the wrappers/enhancers. Just as useQueries() enhances history with the useful fields, iosThrottling() could introduce a throttling mechanism.

Not sure if it should be a part of this particular project though - I guess it depends on how much history project wants to be "we provide the full no-trouble history service" VS "we are just a convenient utility for the browser history".

Another alternative that I will probably take myself is to offload the throttling problem to the clients. While updating URL with every slider move is convenient and simple, certainly I can update it after user releases the slider or do throttling.

Error reporting
A minimal good service would probably be to catch this security exception and add an error message to console suggesting this reason and pointing to the docs or even this discussion. That will save quite some debugging time for the next person who stumbles into it.

@designbyadrian
Copy link

In my tests I was calling pushState once (1) a second, and still had the error thrown after around 100 push states.

@taion
Copy link
Contributor

taion commented Jul 15, 2016

This is now also causing problems for our test suite. If we add too many test cases, we hit this limit and fail on Safari.

It's hard to say what to do here. Throttling would slow down our test suite quite a bit, but having false failures is no good either.

@mjackson
Copy link
Member

@taion It looks like the tests are still running ok on iOS 9.3. See https://travis-ci.org/ReactTraining/history/builds/145156748#L953

@taion
Copy link
Contributor

taion commented Jul 16, 2016

On v3, for #328, the tests failed when I duplicated them between both styles of enhancers: https://travis-ci.org/ReactTraining/history/builds/144871062

On v2, the tests as-is fail: https://travis-ci.org/ReactTraining/history/builds/144879254 (#330)

@mjackson
Copy link
Member

mjackson commented Jul 16, 2016

@taion Hmm, looks like you're right. Man, what a pain.

The solution seems to be two-fold:

  1. Emit a warning when we encounter this exception
  2. Provide an option that reloads the page when we encounter this exception

Unfortunately we won't actually be able to test the refresh behavior or take advantage of it in our test suite since karma can't run a test suite across page reloads. So we'll always need to keep our test suite under 100 pushStates ... 😞

But it should work as a practical workaround for apps.

@taion
Copy link
Contributor

taion commented Jul 16, 2016

It might just not be a big deal for apps unless you're doing something like triggering transitions based on a slider, and not debouncing it. Otherwise there would be a lot more people complaining on this thread about their transitions breaking.

@landabaso
Copy link

landabaso commented Jul 27, 2016

Thanks for working on a workaround for this issue.

I can confirm the problem on the 100th pushState even when called on interval windows larger than 30 secs (tests done on iOS 9.3.3). Thus, all the apps developed with react-router will eventually break after some use. (In my case the SPA is unusable after reaching the 100th click).

@mjackson
Copy link
Member

Anyone wanna work on the solution I suggested in #291 (comment) ? I'll probably get to it eventually, but it's not high-priority for me.

@ChrisBellew
Copy link

I can also confirm that on the 100th push state, regardless of how often, the DOM exception is fired on iOS Safari and not OSX Safari.

@mjackson
Copy link
Member

Closing due to inactivity.

@lock lock bot locked as resolved and limited conversation to collaborators Jun 5, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

8 participants