Anuj More
Anuj More

Reputation: 22121

Set element value in iFrame - Chrome Extension

I am trying to set the active tab Url to a form field within an Iframe that I have injected on chrome.browserAction.onClicked.

The problem is that I cannot access the iframe from the content script because of security policies (Different origin of main page and iframe). The main web page is an external page. eg. wikipedia.com

This is what I have tried so far.

content_script.js

function onExtensionMessage(request) {
  if (request['iconClicked'] != undefined) {
    var extensionOrigin = 'chrome-extension://' + chrome.runtime.id;
    if (!location.ancestorOrigins.contains(extensionOrigin)) {
        var urlvalue = location.href;
        var iframe = document.createElement('iframe');
        iframe.setAttribute("id", "iFrameS");
        iframe.src = chrome.runtime.getURL('popup.html');
        iframe.addEventListener('load', function (e) {
            // Either set the value of input element in Iframe 
            // here or pass an event to background.js and set it from there
            chrome.extension.sendRequest({'frameloaded': true});
        }, false);                       
        document.body.appendChild(iframe);
    }
    return;
  }
}

function initContentScript() {      
  chrome.extension.onRequest.addListener(onExtensionMessage);
}

initContentScript();

popup.html

<form method="post">
    <input id="url" type="text">
</form>

When the extension icon has been clicked, I pass a message to the content script to inject the iframe, once it is loaded, I would want to set the field value.

I have referred this link Access iframe from content script but could not find a solution.

Any help would be appreciated.

Upvotes: 1

Views: 2107

Answers (3)

gaetanoM
gaetanoM

Reputation: 42054

Because you want to set a value inside your Iframe created dynamically in a webpage in Chrome and you need to do this using only javascript you need to refer to postMessage to dispatch your data from the main page to the Iframe.

Immagine your main page like:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <style>
        iframe {
            background: #FFF;
            border: 3px solid #000;
            width: 100%;
            height: 50%;
        }
    </style>
    <script>
        window.onload = function() {
            document.getElementById('titleH2').textContent = 'Main page: ' + window.location.href;
            var myIframe = document.getElementById('myIframe').contentWindow;
            document.getElementById('btnSend').addEventListener('click', function(e) {
                e.preventDefault();
                var dataToSendToIframe = {
                    inputValue: document.getElementById('testToSend').value,
                    inputId: 'url'
                }
                // post message to the Iframe running on other domain
                myIframe.postMessage(dataToSendToIframe, 'http://localhost:63342');
            });
        }
    </script>
</head>
<body>
    <h2 id="titleH2">Main page:</h2>
    <p>
        <input id="testToSend" type="text">
    </p>
    <p>
        <button id="btnSend">Send Message</button>
    </p>

    <iframe id="myIframe" src="http://localhost:63342/Projects/StackOverflow/z.html">
  <p>Your browser does not support iframes.</p>
</body>
</html>

In this page you see an Iframe pointing to a different domain so you cannot act directly on the content inside for security reasons (refers to CORS). Using the postMessage you can send data to your Iframe simply:

myIframe.postMessage(dataToSendToIframe, 'http://localhost:63342');

On the other side, the Iframe (because you can controll it) must contain or be like to:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <style>
    </style>
    <script>
        window.onload = function() {
            document.getElementById('titleH1').textContent = 'Iframe on different domain: ' + window.location.href;
            var inputUrl = document.getElementById('url');
            function receiveMessage(e) {
                var origin = e.origin || e.originalEvent.origin;
                // verify the message arrive from the right origin, if not reject
                if (e.origin !== "http://localhost:33232")
                    return;
                document.getElementById(e.data.inputId).value = e.data.inputValue;
            }
            window.addEventListener('message', receiveMessage);
        }
    </script>
</head>
<body>
<h1 id="titleH1">Iframe on different domain</h1>
<form method="post">
    <input id="url" type="text">
</form>
</body>
</html>

In the Iframe you can see the listener for incoming messages:

function receiveMessage(e) {

You, in the listener, have to use the domain/origin to controll if accept or not the incoming message.

In the following a snapshot (from main page I write something in the input field and press the button and so a postmessage sends this data to the iframe running on a different domain setting the input field):

enter image description here

Upvotes: 1

Emrys Myrooin
Emrys Myrooin

Reputation: 2231

Dealing with IFrames and Content Script is not an easy task. There isn't a very official and good way to do it.

Principle

When I have to do some action in a particular IFrame and not eventual other loaded IFrame, I put a condition at the begining of the Injected Content Script.

if(_.contains(window.location.href, "myIframePage.html")) runOnIframe()

function runOnIframe()
{
    // Do what you want
}

This way the runOnIframe function will only run on the iframe that is loading myIframePage.html


Exemple

Let a page with tow IFrames. The first one load http://google.com/ and the second one chrome.runtime.getURL('popup.html') (your IFrame of course).

The page popup.html contains an <input id="myInput" name="myInput">.

To be able to modify the value of this input from the background script, you have to send a message to correct iframe. Chrome API doesn't allow (until future updates) to send a message to a spécifique IFrame in a page.

The trick is to do that in your injected Content Script :

contentScript.js

if(_contains(window.location.href, "myIframePage.html")) runOnIframe

function runOnIframe()
{
    chrome.extension.onRequest.addListener(onExtensionMessage);
}

function onExtensionMessage(request)
{
    if(request.destination !== "iframe.popup.html") return

    document.getElementById("myInput").value = request.value
}

And in your background page, you simply have to send the object :

{
    destination : "iframe.popup.html",
    value : "Some value to put in the input"
}

The content of the destination can be what you want, it's only to recognize for who you have sent the message. This way you can send message to diferent iframe in the same page by changing this field.

Upvotes: 0

Wolf War
Wolf War

Reputation: 1563

if you need to pass simple data to your newly created iFrame, you can use query string in src of your iFrame.
when creating your iFrame:

var winLoc = window.location.href; //I think this is what you want to send to your iFrame and populate field value

iframe.src = chrome.runtime.getURL('popup.html?'+winLoc );

in your iFrame script split url:

var activeUrl = location.href.split('?')[1];  
//activeUrl now contains passed data

this way you can concatenate as many "simple" data as you like

Upvotes: 2

Related Questions