IE9 infinite $digest bug if loading a base URL without a trailing slash #11439
Description
I'm not honestly sure that this is reproducible in a plunker due to the specific circumstances, and unfortunately our app is secured and internal-only, so I'm not sure I can actually make a ready-to-use example.
IE9 reports infinite $digest loops in HTML5 mode when going to the base URL of an app with a default route in place if you don't use a trailing slash. This does not happen in browsers that actually support the History API and don't need the hashbangs.
Say my base URL is http://www.mysite.com/appBase and I have an angular app hosted there with a default route set to go to /Home. If I go to "http://www.mysite.com/appBase/" (note the trailing slash), I get redirected to http://www.mysite.com/appBase/Home and everything is perfectly all right. No errors in the console, etc.
If I instead go to "http://www.mysite.com/appBase" (note no trailing slash), I end up in the infinite $digest loop. Prior to angular 1.3.6, this case ended up pegging IE so that you had to force-kill (fun!). After 1.3.6 (I've tested up through 1.3.15), we still get an infinite $digest loop but it does finally bail out and succeed, so the problem is more on the order of performance and error log flooding rather than complete disaster like it was prior to 1.3.6. (In fact, I thought this was fixed for some time, but I must only have been testing locally and not on our deployed server with the separate base URL.)
It appears to have something to do with the $$rootScope.$watch(function $locationWatch()) call. I think what's happening (and mind you, debugging anything in IE9 on a deployed server is akin to self-inflicting a root canal) is that something is losing track of the hash symbol. I end up seeing calls to the $browser.url() setter that go back and forth between http://www.mysite.com/appBase/#/ and http://www.mysite.com/appBase/#/Home/. Eventually something changes (I'm wondering if it's simply the $watch bombing due to infinite $digests) to get it out of the cycle, and things in the app are fine.
In $browser.url()'s setter, everything ends up okay if I change the following lines:
if (!sameBase) {
reloadLocation = url;
}
to:
if (replace || !sameBase) {
reloadLocation = url;
}
"replace" is getting set to true twice in a row, the first time to http://www.mysite.com/appBase/#/ and the second time to http://www.mysite.com/appBase/#/Home/. Something is checking $browser.url()'s getter, and with that change, that place is seeing the changed reloadLocation variable after the second call with replace=true and ends up cutting out of the $digest loop.
I don't know if there are any other implications to that change, though, or if this is the right way to go about this. Without the ability to step through in a real debugger (since IE9's debugger seems to be useless for SPAs), I'm limited to what I've been able to log to the console or pop up in an alert (since apparently console.log doesn't always fire in IE9?!?). All I know is that Chrome and Firefox are still fine after that change as well, and I can't think of a compelling reason that we should hang on to the first "reloadLocation" if we're doing a "replace" twice.
I hope this makes at least some sense to someone. Again, if I had the means to make a reproducible testcase, I would, but it seems to be something that's hard to emulate without free reign to muck up a public server.