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 lesframes['frameName']
, (se référant normalement à l'objet dewindow
global contenant la trame) sontundefined
.var iframe = document.getElementById('frameName');
iframe.contentDocument
renvoie un objet dedocument
du cadre contenant, car les scripts de contenu ont accès au DOM d'une page. Cette propriété estnull
lorsque la même politique d'origine s'applique.iframe.contentDocument.defaultView
(se réfère à l'objet dewindow
associé au document) est indéfini .iframe.contentWindow
est indéfini .
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);
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é).