-1

I just learned about the rel="noopener" attribute, which makes the opened windows unaware of their opener. I played with it a bit and got window.opener === null all the time, until I found out that it's now the new default in all modern browsers.

Now, I'd like to actually read the window.opener property to see how it works:

<!-- http://opener-from.test/ -->
<a href="http://opener-to.test/" target="_blank">no rel</a>
<a href="http://opener-to.test/" target="_blank" rel="opener">rel="opener"</a>
<a href="http://opener-to.test/" target="_blank" rel="noopener">rel="noopener"</a>
<!-- http://opener-to.test/ -->
<script>
console.log(window.opener);
</script>

The no rel and rel="noopener" links just make the destination get window.opener === null. As for the rel="opener", it works great it Firefox and Safari, but in Google Chrome I'm getting the error:

Uncaught DOMException: Blocked a frame with origin "http://opener-to.test" from accessing a cross-origin frame.

I tested it in Chrome 91. How can I access the window.opener in the context of target="_blank" in Google Chrome?

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
Robo Robok
  • 21,132
  • 17
  • 68
  • 126
  • What's not clear about the (source of the) error message - _"**Blocked a frame** with origin "http://opener-to.test" **from accessing a cross-origin frame**"_ ? – Andreas Jul 03 '21 at 08:58
  • The question is how to allow that from the `opener-to.test`. – Robo Robok Jul 03 '21 at 09:00
  • See https://stackoverflow.com/questions/25098021/securityerror-blocked-a-frame-with-origin-from-accessing-a-cross-origin-frame – sideshowbarker Jul 03 '21 at 09:15
  • @sideshowbarker I've seen this post before writing mine. Still don't get it why Chrome throws an error, while other browsers don't. – Robo Robok Jul 03 '21 at 09:18

1 Answers1

0

This has nothing to do with the default of rel, it has to do with cross-origin access. From MDN's documentation for opener:

If the opener is not on the same origin as the current page, functionality of the opener object is limited. For example, variables and functions on the window object are not accessible. However, navigation of the opener window is possible, which means that the opened page can open an URL in the original tab or window.

(my emphasis)

console.log(window.opener) will try to access properties on window.opener.

Having rel="opener" makes it possible to do navigation in the opener, but not to access global variables (including functions) exposed by that window. It also makes it possible to use postMessage to talk to the opener window.

If you just want to see whether you have access to opener, do console.log(window.opener === null) instead and see whether it's true (you don't have access) or false (you do have limited access).

Even with rel="opener", though, it's limited access as indicated by the MDN quote above. You cannot get full access to window.opener cross-origin (only same origin).

This ability to allow only limited access to the object's contents isn't just magic that browsers can do, you can do it in JavaScript itself via Proxy:

const realOpenerWindow = {
    x: 42,
};

const opener = new Proxy(realOpenerWindow, {
    get(target, property, receiver) {
        if (allow) {
            return Reflect.get(target, property, receiver);
        }
        throw new Error(`Access is disallowed`);
    }
})


let allow = true;
console.log(opener === null); // false
console.log(opener.x);        // 42
allow = false;
console.log(opener === null); // false
console.log(opener.x);        // Throws error

Regarding why Chrome gives you the error but non-Chromium browsers like Firefox don't: Different consoles are implemented differently. Apparently Firefox's console is written to show you what it's allowed to show you, but Chrome's just applies its usual object handling and triggers the access error.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Thank you, I'm not sure if it answers my main question here. I'm just learning about it and accessing `window.opener` in Chrome throws an error. It doesn't throw in other browsers. I'm not yet inspecting what's in this object and what's not. – Robo Robok Jul 03 '21 at 09:08
  • @RoboRobok - Yes, you are -- as I said, `console.log` will try to access the properties of the window, because that's what it does when you pass it an object. If you just want to see whether you have access to `opener`, do `console.log(window.opener === null)` to see whether it's true or false. – T.J. Crowder Jul 03 '21 at 09:09
  • Again, how would I do it if it throws an error in Chrome? Did I word my question that poorly? – Robo Robok Jul 03 '21 at 09:12
  • @RoboRobok - Again, `console.log(window.opener === null)`. – T.J. Crowder Jul 03 '21 at 09:13
  • Okay, so I see it prints `false` in the console for Chrome. But why? It's the first time I see a value throwing an error in `console.log()` when used directly, but not as an expression. My question still remains unanswered. Is it possible to allow accessing `window.opener` in Chrome through CORS configuration? – Robo Robok Jul 03 '21 at 09:16
  • @RoboRobok - Please read the answer above carefully. Having `rel="opener"` grants you ***limited*** access to `window.opener`, enough to make it navigate and to use `postMessage`. You cannot get full access to `window.opener` cross-origin. (I'll add that last bit, it's not there.) – T.J. Crowder Jul 03 '21 at 09:18
  • So I guess the real question here for me would be: why does the `console.log(window.opener)` throw an error, (in Chrome only) while `console.log(window.opener === null)` doesn't. It's some weird behavior I have never seen before, I thought accessing something either throws an error or not, no matter the expression. – Robo Robok Jul 03 '21 at 09:21
  • @RoboRobok - Because *"...`console.log(window.opener)` will try to access properties on `window.opener`...."* and *"...variables and functions [e.g., properties] on the window object are not accessible..."* Basically, some parts of the object `opener` refers to are protected, but `console.log` tries to access them. You can access `opener`, itself, but not some of its properties. – T.J. Crowder Jul 03 '21 at 09:24
  • Yes, it's really interesting. I'd expect Chrome to just print that limited `window.opener` with only the properties I'm allowed to see. I wonder if I can reproduce this error in other browsers, perhaps by trying to explicitly read something from `window.opener`. As a reminder: I asked this question to learn about this behavior, so I'm interested in every detail :) – Robo Robok Jul 03 '21 at 09:26
  • @RoboRobok - Here's a way you can explore it further: In your parent window, create a global `var` (not `let` or `const`) called `x` with a value (say, 42). In the child window, try this: `console.log(window.opener === null); console.log(window.opener.x);` The first will show you `false` (because you have `rel="opener"`) but the second will cause an error (or possibly show you `undefined`). Try it again when they're in the same origin, and you'll see `42` from the second line. Enjoy exploring! :-) – T.J. Crowder Jul 03 '21 at 09:28
  • Just for fun, I've added an example of how you can do this in JS to the answer, it's not just for browsers, but what they do is conceptually similar. – T.J. Crowder Jul 03 '21 at 09:34
  • 1
    I will experiment a bit more and let you know how it went :) thanks so far! – Robo Robok Jul 03 '21 at 09:55
  • Okay, I have experimented a bit and it looks like both Safari and Firefox throw an error when I try to read a property on the `window.opener` object. So the difference between Chrome and them is that Chrome doesn't even allow `console.log(window.opener)`, while the other two allow to inspect the object itself while we're not explicit about it. – Robo Robok Jul 03 '21 at 21:48