Google Chrome: Accessing window.opener for target="_blank" link after rel="noopener" is the new default

1.7k Views Asked by At

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?

1

There are 1 best solutions below

13
T.J. Crowder On

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.