How to postMessage into iFrame?

Kostiantyn Ivashchenko picture Kostiantyn Ivashchenko · May 1, 2020 · Viewed 14.3k times · Source

I am developing a Chrome extension to use with my own Web-application, processing Web-pages (including cross-domain ones), loaded into iFrame on my application's main page. So I have to send message, containing the URL of the page to be processed, from my main page to the content script, injected into the target iFrame to get access to the processed page's window.

I try to implement the approach, described here. Below are the essential parts of all files involved:

index.html:

...
<head>
    ...
    <script type="text/javascript" src="base.js"></script>
</head>
<body>
    <div>
        <input type="text" id="page_url">&nbsp;<input type="button" id="get_page" value="Get page">
    </div>
    <iframe id="cross_domain_page" src=""></iframe>
</body>

base.js:

(function() {
    "use strict";
    function pageProcessing() {
        let urlButton = window.document.getElementById("get_page");
        urlButton.addEventListener("click", () => {
            let url = window.document.getElementById("page_url").value;
            window.postMessage(
                {
                    sender: "get_page_button1",
                    message: url
                },
                "*"
            );
            window.postMessage(
                {
                    sender: "get_page_button2",
                    message: url
                },
                "*"
            );
        });
    }
    window.document.addEventListener('readystatechange', () => {
            if (window.document.readyState == 'complete') {
                pageProcessing();
            }
        }
    );
})();

manifest.json:

{
  "manifest_version": 2,
  "name": "GetPage",
  "version": "0.1",
  "content_scripts": [
    { "js": ["main.js"], "matches": ["<all_urls>"] },
    { "js": ["frame.js"], "matches": ["<all_urls>"], "all_frames": true, "match_about_blank": true }
  ],
  "permissions": ["activeTab"]
}

main.js:

(function() {
    "use strict";
    window.isTop = true;
})();

frame.js:

(function() {
    "use strict";
    window.addEventListener("message", (event) => {
        if (event.data &&
            event.data.sender == "get_page_button1") {
            if (window.isTop) {
                alert("Main window alert");
            } else {
                alert("Frame window alert 1");
            }
        }
    });
    if (!window.isTop) {
        window.addEventListener("message", (event) => {
            if (event.data &&
                event.data.sender == "get_page_button2") {
                alert("Frame window alert 2");
            }
        });
    }
})();

The problem is that when I click "get_page" button the only alert I see is the main window alert. As for my understanding it means that the message, posted from the main window, does not reach the content script, injected into iFrame.

What is wrong with my script and how to fix the problem?

Answer

wOxxOm picture wOxxOm · May 1, 2020

window.postMessage in your web app sends to the main document's window, not to the iframe's.

Specify the iframe's window object:

document.getElementById('cross_domain_page').contentWindow.postMessage(.......)

Alternatively, you can switch to the more secure externally_connectable messaging.