krakenjs/post-robot

IE - access is denied

loxy opened this issue · 11 comments

loxy commented

Hi,

I get an access denied error in an angular application on Internet Explorer 11, chrome works fine. The docs say:

Unfortunately, IE blocks direct post messaging between a parent window and a popup, on different domains.

But we are not using popups (I think you mean window.open(...), right?). So, is it always necessary to use the suggested "bridge system" when running on IE?

Hi @loxy,

Yeah, if you're just using iframes, you should be fine -- you should not need the bridge.

Can you let me know:

  • A quick example of your post-robot code?
  • Can you make sure IE is not running in any kind of compatibility mode?
  • Is there any chance IE is running in intranet mode? You can run the following function to find out:
(function() {
    window.status = 'testIntranetMode';
    console.log('intranet mode:', window.status === 'testIntranetMode');
})();
loxy commented

It seems so that we are running in intranet mode. Your function returned true.
We are not running any compatibility mode:
<meta http-equiv="X-UA-Compatible" content="IE=EDGE" />

We are running a portal app which includes several apps in an iframe, always one at a time. Now we want to synchronize the URL of the portal with each app and vice versa. One can say the URL is divided into two parts: portal part and app part.

So when we load a new iframe (an app) we emit a TRY_TO_NAVIGATE event to the app with the proper app part URL. If the location changed somehow the iframe is not loaded again, if it is a link to the same app. This is of course not true for a link to an unloaded app.

It all works great on first load and when switching between the same app. Even for the second app loaded. But as soon as a third app comes into play (even if it was the first loaded app) it breaks. To illustrate:

Apps: A, B, C
States in App A: A_1, A_2, ... (same goes for other apps)

A_1 -> A_2: works
A_1 -> B_2 -> B1: works
A_1 -> B2_ ->A1: broken
A_1 -> B2_ ->C1: broken

The call to post robot look like this:

onApplicationLoad(contentWindow: Window) {
    console.debug(`[AppHolderController] - ${this.src} completely loaded`);
    this.currentWindowService.currentWindow = contentWindow;
    robot
      .send(contentWindow, NavigationCommand.TRY_TO_NAVIGATE, {url: '/' + this.$stateParams.tail})
      .catch(function (err) {
        console.error(`[AppHolderController] - ${NavigationCommand.TRY_TO_NAVIGATE} failed:`, err);
      });
  }

The function is called when the load event of the iframe is fired...

Is the intranet mode problematic? Never heard of it before. Fucking IE...

Intranet mode has been problematic in weird and interesting ways, yes. If I can try to repro the exact scenario I can see how fixable it is though.

Question -- are these frames and the parent page all on the same domain, or multiple different domains?

loxy commented

At least different ports, but it should work on different domains too. I can provide a sample repo, but it is written in typescript, compiled through webpack.

loxy commented

Ok, i tried to create a minimal sample repo. Here is the link: https://github.com/loxy/ie-intranet-bug
If you have questins feel free to contact me...

loxy commented

Ups, I had a bug in the compilation process. Now it should work. But tried the app at home in a non intranet IE11. Same problem, so it has nothing to do with this mode...

Hmm strange -- sorry for the delays in trying this, got a lot on my plate atm but I'll try to get to it soon.

loxy commented

Any updates on this?

OK, I cloned your repo, repro'd the issue, and added a fix. I'm no longer seeing errors from post-robot -- can you try installing the latest (7.0.12) and let me know if you still see the issue?

If you're interested, the issue I found was this: (recording for my own sanity next time something like this happens)

  • post-robot relies pretty heavily on WeakMap to keep references to windows without leaking memory for closed windows
  • We wrote our own polyfill for WeakMap for older browsers: https://github.com/krakenjs/cross-domain-safe-weakmap -- since none of the existing polyfills seem to take into account that the WeakMap key might be a cross-domain window object, which will error out when you try to attach any properties to it.
  • In order to find if a window is a valid key for a given WeakMap instance, we do something like if (keys.indexOf(win) !== -1)
  • Even though indexOf should just be doing strict equality checks in indexOf, it turns out that if one of the windows in the keys array is closed or destroyed in a particular way, some weird stuff happens:
    • keys[i] gives undefined, even though there was originally a window object at that index (woot, references being changed transparently!).
    • keys.indexOf(something) always throws a Permission denied error. I'm wondering if IE is doing something weird like trying to dig into the properties of the object and erroring out, rather than just sticking to a strict equality check? And of course, this permission denied error has no stack trace to speak of...
    • Incidentally, this seems to explain why the issue for you only happened after some windows/frames have been opened/closed
  • cross-domain-safe-weakmap was doing a cleanup run over keys for set, delete etc. to check for closed windows, but not for get since this is normally more of a hot path.
  • Fix for now was to do that cleanup run even for get, since it's a slow path anyway (normally cross-domain-safe-weakmap will rely on native WeakMap if it's available)

TLDR: IE is an awful, awful browser. But we knew that anyway right?

Sorry again for the delay in getting to this. Appreciate the thorough example, it really helped!

loxy commented

Great! It's working now. Thank you for the detailed explanation!
And yes, we all love it...

@Mischi: It's time to switch, right?

Sweet!