javascriptgoogle-chromesecurityiframecross-origin-opener-policy

Cross-Origin-Opener-Policy and window.open returning null in a cross origin iframe even though it opened successfully


I have a case of where

Then the return value of window.open is null, even though the tab opens successfully.

For example if

Then a new tab opens showing the Google home page, but I'll see null in the console on the original tab.

from typing import Union

from fastapi import FastAPI, Response
from fastapi.responses import HTMLResponse

app = FastAPI()

@app.get("/", response_class=HTMLResponse)
def read_root(response: Response):
    response.headers["Cross-Origin-Opener-Policy"] = "same-origin"
    return '''
        <iframe src="http://localhost:8001/iframe"></iframe>
    '''

@app.get("/iframe", response_class=HTMLResponse)
def read_iframe(response: Response):
    response.headers["Cross-Origin-Opener-Policy"] = "same-origin"
    return '''
        <button id="button">Open</button>
        <script>
            document.getElementById("button").addEventListener("click", () => {
                const w = window.open('https://www.google.com/', "_blank");
                console.log(w);
            });
        </script>
    '''

However if I visit the iframe directly by going to http://localhost:8001/iframe and do the same thing, I see what seems to be a Window(/Window proxy?) object in the console.

Or: if I make the iframe be on the same domain as the parent page, I'll also see a Window/Window proxy object in the console.

Or: if I change same-origin to same-origin-allow-popups, I'll also see a Window/Window proxy object in the console.

Why in the case of a cross origin iframe with Cross-Origin-Opener-Policy set to same-origin does window.open return null? At https://developer.mozilla.org/en-US/docs/Web/API/Window/open it says

null is returned if the browser fails to open the new browsing context, for example because it was blocked by a browser popup blocker.

But it did open the browsing context...

The reasons why I want to understand: I have a similar setup on a site, and I'm deciding between Cross-Origin-Opener-Policy: same-site and Cross-Origin-Opener-Policy: same-site-allow-popups. So, I need to understand what it does. However, window.open's behaviour. depending on whether it's in a cross domain iframe or not is slightly thwarting that understanding


Solution

  • Why in the case of a cross origin iframe with Cross-Origin-Opener-Policy set to same-origin does window.open return null?

    It's in the spec. In this exact situation it makes window.open behave as though you passed noopener to it, which in turn makes window.open return null.

    In more detail, from The rules for choosing a navigable

    1. If currentDocument's cross-origin opener policy's value is "same-origin" or "same-origin-plus-COEP", and currentDocument's origin is not same origin with currentDocument's relevant settings object's top-level origin, then:
      1. Set noopener to true.
      2. Set name to "_blank".
      3. Set windowType to "new with no opener".

    and then from 7.2.2.1 Opening and closing windows, when it's describing what window.open does

    1. If noopener is true or windowType is "new with no opener", then return null.

    You can also see how window.open behaves with noopener - with no COOP and no iframe involved - it returns null just as in the case of this question.

    from typing import Union
    
    from fastapi import FastAPI, Response
    from fastapi.responses import HTMLResponse
    
    app = FastAPI()
    
    @app.get("/", response_class=HTMLResponse)
    def read_root(response: Response):
        return '''
            <button id="button">Open</button>
            <script>
                document.getElementById("button").addEventListener("click", () => {
                    const w = window.open('https://www.google.com/', "_blank", "noopener");
                    console.log(w);
                });
            </script>
        '''