2017-09-01 2 views
5

Je voudrais exécuter du javascript dans le contexte d'une fenêtre iframe. En ce moment, la seule façon que je peux penser à faire est d'injecter une balise de script:Exécuter le code dans le contexte de la fenêtre frame

myIframe = document.createElement('iframe'); 
myIframe.setAttribute('name', 'xyz123'); 
document.body.appendChild(myIframe); 

myIframe.contentWindow.document.write(` 
    <script> 
     console.log('The current window name is:', window.name); 
    </script> 
`); 

Note: Ceci est un iframe même domaine, sans Src, donc j'avoir un accès complet à la contentWindow.

Il est important pour mon utilisation que le code fonctionne avec les globals corrects; window, document etc devraient tous être portée à l'iframe lui-même.

Y at-il un autre moyen de le faire? Les travaux ci-dessus, mais le script doit fonctionner sur différents domaines toutes les règles de CSP différentes, ce qui signifie l'ajout du support pour nonces/hash etc.

Est-il possible de faire quelque chose comme:

myIframe.contentWindow.run(function() { 
    console.log('The current window name is:' window.name); 
}); 

Je J'ai essayé myIframe.contentWindow.setTimeout mais cela semble toujours exécuter le code dans le contexte de la fenêtre parente.

Répondre

2

Peut-être scinder le javascript en pièce de la fenêtre principale (appelons-le main.js) et de iframe (appelons-le iframe.js). Puis dans le lien src iframe lieu à iframe.js ou iframe.html qui charge le fichier js (je ne suis pas sûr si vous pouvez inclure javascript directement à partir de src attribut).

Si vous chargez js dans iframe, utilisez la solution au Calling a function inside an iframe from outside the iframe.

+0

Merci pour la suggestion - mais pour mon cas d'utilisation, je dois pouvoir insérer javascript arbitraire dans le cadre, qui n'est pas nécessairement hébergé n'importe où - d'où le tag de script. – bluepnume

2

Vous pouvez réellement créer cette fonction run, puis appliquer une fonction de rappel à this, qui bien sûr sera le contexte iframe. Ensuite, vous pouvez accéder à des éléments iframe en utilisant this:

myIframe.contentWindow.run = function(fn) { 
    fn.apply(this); 
}; 

myIframe.contentWindow.run(function() { 
    console.log('(run) The current window name is:', this.window.name); 
}); 

Sortie de la console

(run) The current window name is: xyz123 

Vous pouvez consulter mon exemple ici: http://zikro.gr/dbg/html/con-frame/

EDIT

Si vous voulez juste utiliser window plutôt que this.window, vous pouvez créer un paramètre à la fonction en ligne avec e nom window, et puis juste passer this.window à cette fonction comme ceci:

myIframe.contentWindow.run = function(fn) { 
    fn.call(this, this.window); 
}; 

myIframe.contentWindow.run(function(window) { 
    console.log('(run) The current window name is:', window.name); 
}); 

Et il fonctionne toujours comme prévu.

+0

Ceci est un bon exemple - seul le problème est, il change la sémantique de toutes les fonctions qui existeront dans l'iframe pour faire référence à 'this.window' plutôt qu'à' window' - mais c'est certainement la meilleure réponse loin. Merci! – bluepnume

+0

@bluepnume vous êtes les bienvenus. J'ai mis à jour ma réponse pour avoir une fonction inline qui a un paramètre avec le nom 'window' que vous pouvez utiliser; cela élimine le besoin d'utiliser 'this.window'. Cela fonctionnera à moins que vous ayez besoin que la fonction inline entière soit explicitement appliquée à partir d'une source spécifique qui ne sera alors pas possible d'avoir ce paramètre spécifique. –

1
window.name='main window'; // just for debugging 
var myIframe = document.createElement('iframe'), frameScript = document.createElement('script'); 
document.body.appendChild(myIframe); 
frameScript.textContent = "window.top.name='topWindow';window.name = 'xyz123';function WhereAmI(){return(window.name);} window.parent.postMessage('frame_initialized', '*'); " 
myIframe.contentWindow.document.documentElement.appendChild(frameScript); 
function OnMessage(event) { 
    if(typeof event.data == 'string') switch(event.data) { 
    case 'frame_initialized': 
    myIframe.contentWindow.document.body.appendChild(document.createTextNode(myIframe.contentWindow.WhereAmI())); 
    console.log('Now running in', myIframe.contentWindow.WhereAmI()); 
    break; 
    } 
} 
window.addEventListener('message', OnMessage, false); 

Testé avec Firefox et Chrome. Au lieu de .textContent, vous pouvez appliquer le fichier .src à frameScript, afin que le script puisse se charger de manière asynchrone. Ensuite, vous pouvez appeler postMessage comme indiqué ci-dessus ou appeler une fonction de rappel pour notifier la fenêtre parente.

Notez que dans votre code d'origine window.frameElement.name est initialisé.Votre script demande alors window.name. FireFox copie automatiquement la valeur dans la fenêtre, ce qui crée une certaine confusion.

+0

Pour améliorer cette réponse, je dois mieux comprendre votre scénario. Avez-vous accès au code HTML sur les deux domaines? Devrait-il fonctionner uniquement pour vous localement ou pour tout le monde? L'utilisation du gestionnaire de script comme GreaseMonkey est-elle possible? – gnblizz

+0

Merci pour la suggestion, mais je ne suis pas sûr que window.postMessage m'aiderait spécifiquement à exécuter du code arbitraire dans le cadre - j'aurais besoin d'un moyen d'ajouter un écouteur de message dans le cadre pour que cela fonctionne, ce qui m'amène Retour au problème original. – bluepnume

+0

Pour répondre à votre question, cela doit fonctionner pour tout le monde afin que greasemonkey ne fonctionne pas. Il n'y a pas de second domaine - l'iframe est chargé sans un 'src' – bluepnume

1

Vous devez charger le script de manière asynchrone (c'est-à-dire $.getScript()), puis l'appeler sur .contentWindow. Je n'ai pas testé mais cela devrait fonctionner.

(function() { 

    var myIframe = document.createElement('iframe#iframe'): 

    var jsSnippet; 
    var iframeScript = function(url) { 
     script = document.createElement('script'), 
     scripts = document.getElementsByTagName('script')[0]; 
     script.src = url; 
     return jsSnippet = scripts.parentNode.insertBefore(script, scripts); 
    }; 

    myIframe.setAttribute('name', 'xyz123'): 
    myIframe.appendChild(jsSnippet); 
    document.body.appendChild(myIframe); 
    return document.getElementById('#iframe').contentWindow.iframeScript(); 
}) 

Ces deux ressources seront utiles si cette solution ne fonctionne pas: https://plainjs.com/javascript/ajax/load-a-script-file-asynchronously-49/

Invoking JavaScript code in an iframe from the parent page