web-dev-qa-db-fra.com

Injection de fonctions JS dans la page à partir d'un script Greasemonkey sur Chrome

J'ai un script Greasemonkey qui fonctionne très bien dans Firefox et Opera. J'ai du mal à le faire fonctionner dans Chrome, cependant. Le problème consiste à injecter une fonction dans la page qui peut être invoquée par le code de la page. Voici ce que je fais jusqu'à présent:

Tout d'abord, j'obtiens une référence d'aide à nsafeWindow pour Firefox. Cela me permet d'avoir le même code pour FF et Opera (et Chrome, je pensais).

var uw = (this.unsafeWindow) ? this.unsafeWindow : window;

Ensuite, j'injecte une fonction dans la page. C'est vraiment juste un wrapper très fin qui ne fait qu'invoquer la fonction correspondante dans le contexte de mon script GM:

uw.setConfigOption = function(newValue) {
    setTimeout(setConfigOption, 0, newValue);
}

Ensuite, il y a la fonction correspondante dans mon script:

setConfigOption = function(newValue) {
    // do something with it, e.g. store in localStorage
}

Enfin, j'injecte du HTML dans la page avec un lien pour appeler la fonction.

var p = document.createElement('p');
p.innerHTML = '<a href="javascript:setConfigOption(1)">set config option to 1</a>';
document.getElementById('injection-point').appendChild(p);

Pour résumer: Dans Firefox, lorsque l'utilisateur clique sur ce lien injecté, il exécutera l'appel de fonction sur unsafeWindow, qui déclenche ensuite un délai d'expiration qui appelle la fonction correspondante dans le contexte de mon script GM , qui effectue ensuite le traitement proprement dit. (Corrigez-moi si je me trompe ici.)

Dans Chrome, je reçois juste une erreur "Uncaught ReferenceError: setConfigOption n'est pas défini". Et en effet, entrer "window.setConfigOption" dans la console donne un "indéfini". Dans Firebug et la console de développeur Opera, la fonction est là.

Il y a peut-être une autre façon de le faire, mais quelques-unes de mes fonctions sont invoquées par un objet Flash sur la page, ce qui, à mon avis, rend nécessaire d'avoir des fonctions dans le contexte de la page.

J'ai jeté un rapide coup d'œil aux alternatives à unsafeWindow sur le wiki Greasemonkey, mais ils ont tous l'air assez moche. Suis-je complètement sur la mauvaise voie ici ou devrais-je y regarder de plus près?

RÉSOLUTION: J'ai suivi les conseils de Max S. et cela fonctionne à la fois dans Firefox et Chrome = maintenant. Parce que les fonctions dont j'avais besoin pour être disponible sur la page devaient être rappelées dans les fonctions normales, j'ai déplacé tout mon script vers la page, c'est-à-dire qu'il est complètement enveloppé dans la fonction qu'il a appelée 'main ()'.

Pour rendre la laideur supplémentaire de ce hack un peu plus supportable, je pourrais au moins abandonner l'utilisation de unsafeWindow et wrappedJSObject maintenant.

Je n'ai toujours pas réussi à faire fonctionner le contenu scope runner du wiki Greasemonkey. Il devrait faire de même et semble fonctionner très bien, mais mes fonctions ne sont jamais accessibles à <a> éléments de la page, par exemple. Je n'ai pas encore compris pourquoi.

38
Henrik Heimbuerger

La seule façon de communiquer avec le code en cours d'exécution sur la page en Chrome est via le DOM, vous devrez donc utiliser un hack comme l'insertion d'un <script> tag avec votre code. Notez que cela peut s'avérer bogué si votre script doit être exécuté avant tout le reste de la page.

EDIT: Voici comment l'extension Nice Alert fait ceci:

function main () {
  // ...
  window.alert = function() {/* ... */};
  // ...
}

var script = document.createElement('script');
script.appendChild(document.createTextNode('('+ main +')();'));
(document.body || document.head || document.documentElement).appendChild(script);
68
Max Shawabkeh

J'ai ceci :

contentscript.js:

function injectJs(link) {
var scr = document.createElement('script');
scr.type="text/javascript";
scr.src=link;
document.getElementsByTagName('head')[0].appendChild(scr)
//document.body.appendChild(scr);
}

injectJs(chrome.extension.getURL('injected.js'));

injected.js:

function main() {
     alert('Hello World!');
}

main();
15
Bernard Choi

J'ai jeté un rapide coup d'œil aux alternatives à unsafeWindow sur le wiki Greasemonkey, mais ils ont tous l'air assez moche. Suis-je complètement sur la mauvaise voie ici ou devrais-je y regarder de plus près?

Vous devriez regarder, car ce n'est que l'option disponible. Je préfère utiliser hack d'emplacement .

myscript.user.js:

function myFunc(){
  alert('Hello World!');
}

location.href="javascript:(function(){" + myFunc + "})()"

example.com/mypage.html

<script>
myFunc() // Hello World!
</script>

Bien sûr, c'est moche. Mais ça marche bien.


La méthode Content Scope Runner, mentionnée par Max S. est meilleure que le hack de localisation, car elle est plus facile à déboguer.

4
NVI

Les autres réponses vous obligent soit à utiliser expressions de fonction , à importer un fichier supplémentaire externe soit à utiliser un long hack patché .

Cette réponse ajoutera le javascript dans la page directement à partir de votre code source. Il utilisera ECMAScript 6 (ES6) littéraux de modèle pour obtenir la chaîne javascript multi-lignes sans effort sur la page.

var script = document.createElement('script'); 
script.type = "text/javascript"; 
script.innerHTML = ` 
   function test() {
      alert(1);
   }
`;
document.getElementsByTagName('head')[0].appendChild(script);

Veuillez noter les astuces `` qui définissent le début et la fin d'une chaîne multiligne.

2
user136036