Accès à l'iframe à partir de l'extension chrome

Je développe une extension chrome et je me suis heurté à un gros problème.

J'utilise des scripts de contenu pour injecter mon code javascript sur un site Web. Le site web a un iframe. Je peux modifier le code source de l'iframe mais je ne semble pas avoir accès à la propriété contentWindow de l'iframe. J'ai besoin d'insérer du texte à la position actuelle de la carrette.

Donc, fondamentalement, ce code fonctionne parfaitement dans le contexte de la page:

$("#iframe1").contentWindow.document.execCommand("InsertHTML", false, 'test text'); 

Mais lorsque j'essaie de l'exécuter dans le contexte de mon extension chrome, j'ai cette erreur:

 TypeError: Cannot read property 'document' of undefined 

Ce qui est étrange, c'est que je peux accéder au html de l'iframe. Donc, ce code fonctionne parfaitement à partir de l'extension chrome:

 $("#iframe1").contents().find('div').html('test') 

J'ai essayé de mettre "all_frames": vrai dans le fichier manifeste mais pas de chance 🙁

Pour comprendre pourquoi votre code ne fonctionne pas, j'inclus un fragment de ma réponse précédente :

Les scripts de contenu n'ont aucun accès à l'objet de window global d'une page. Pour les scripts de contenu, ce qui suit s'applique:

  • La variable de window ne fait pas référence à l'objet global de la page. Au lieu de cela, il se réfère à un nouveau contexte, une "couche" sur la page. Le DOM de la page est entièrement accessible. # Execution-environment

Étant donné un document composé de <iframe id="frameName" src="http://domain/"></iframe> :

  • L'accès au contenu d'une image est restreint par la même politique d'origine de la page; Les autorisations de votre extension ne relâchent pas la politique.
  • frames[0] et les frames['frameName'] , (se référant normalement à l'objet de window global contenant la trame) sont undefined .
  • var iframe = document.getElementById('frameName');
    • iframe.contentDocument renvoie un objet de document du cadre contenant, car les scripts de contenu ont accès au DOM d'une page. Cette propriété est null lorsque la même politique d'origine s'applique.
    • iframe.contentDocument.defaultView (se réfère à l'objet de window associé au document) est indéfini .
    • iframe.contentWindow est indéfini .

Solution pour les cadres de même origine

Dans votre cas, l'un des éléments suivants fonctionnera:

 // jQuery: $("#iframe1").contents()[0].execCommand( ... ); // VanillaJS document.getElementById("iframe1").contentDocument.execCommand( ... ); // "Unlock" contentWindow property by injecting code in context of page var s = document.createElement('script'); s.textContent = 'document.getElementById("iframe1").contentWindow.document.execCommand( ... );'; document.head.appendChild(s); 

Solution générique

La solution générique utilise "all_frames": true dans le fichier manifeste, et utilisez quelque chose comme ceci:

 if (window != top) { parent.postMessage({fromExtension:true}, '*'); addEventListener('message', function(event) { if (event.data && event.data.inserHTML) { document.execCommand('insertHTML', false, event.data.insertHTML); } }); } else { var test_html = 'test string'; // Explanation of injection at https://stackoverflow.com/a/9517879/938089 : // Run code in the context of the page, so that the `contentWindow` // property becomes accessible var script = document.createElement('script'); script.textContent = '(' + function(s_html) { addEventListener('message', function(event) { if (event.data.fromExtension === true) { var iframe = document.getElementById('iframe1'); if (iframe && (iframe.contentWindow === event.source)) { // Window recognised, post message back iframe.contentWindow.postMessage({insertHTML: s_html}, '*'); } } }); } + ')(' + JSON.stringify(test_html) + ');'; (document.head||document.documentElement).appendChild(script); script.parentNode.removeChild(script); } 

Cette démo est uniquement à des fins éducatives, n'utilisez pas cette démo dans une véritable extension . Pourquoi? Parce qu'il utilise postMessage pour transmettre des messages. Ces événements peuvent également être générés par le client, ce qui provoque une fuite de sécurité (XSS: injection HTML arbitraire).

L'alternative à postMessage est l'API de message de Chrome. Pour une démonstration, voir cette réponse . Vous ne pourrez toutefois pas comparer les objets de la window . Ce que vous pouvez faire, c'est de s'appuyer sur la propriété window.name . La propriété window.name est automatiquement définie sur la valeur de l'attribut name de l'iframe (juste une fois, lorsque l'iframe est chargé).