web-dev-qa-db-fra.com

Chrome définie sur `run_at`` document_start` fonctionne trop vite?

EDIT: quelque chose n'allait pas avec mon Chrome et créant un conflit avec mon script, une réinstallation complète a éliminé la source du problème. Si je découvre la cause du problème, je l'inclurai ici.

EDIT2: Juste pour faire savoir à quiconque lisant ceci en 2017 que je n'ai pas oublié cela et que je n'ai jamais eu ce problème depuis ma précédente édition.

EDIT3: Nous sommes en 2019 et jusqu'à présent, je n'ai plus jamais eu ce problème.


J'ai appris à créer une simple Chrome qui est un port de script utilisateur. Le script fonctionne parfaitement avec Tampermonkey avec le paramètre run at À document-start, Tous les événements nécessaires qui doivent être capturés depuis le début sont tous capturés.

Cependant, lorsque j'ai défini les mêmes paramètres dans l'extension Chrome extension, j'ai découvert que le même paramètre de fonctionnement est plus rapide que celui de Tampermonkey, ce qui entraîne l'échec de la première fonction: (Uncaught TypeError: Cannot call method 'appendChild' of null.) Car il essaie d'ajouter un élément de script à la section head, qui n'existe que jusqu'à 0,010 s plus tard.

Jusqu'à présent, ma sale solution a été d'utiliser une fonction setInterval avec le minuteur réglé sur 10 pour vérifier si document.head Existe et ensuite continuer avec le code si la condition est vraie.

Existe-t-il un moyen de faire fonctionner cela correctement sans avoir à recourir à setInterval ou peut-être répliquer l'option grant none De Tampermonkey qui semble exécuter le script utilisateur dans le contexte de la page Web?

Voici mon fichier manifest.json:

{
    "manifest_version": 2,
    "content_scripts": [ {
        "js":        [ "simpleuserscript.user.js" ],
        "matches":   [ "https://www.google.com/*"],
        "run_at":    "document_start"
    } ],
    "converted_from_user_script": true,
    "description":  "Chrome extension",
    "name":         "Testing",
    "version":      "1"
}

Tout cela pourrait être évité si Chrome adopterait l'événement afterscriptexecute, mais jusqu'à ce que cela se produise, je suis bloqué avec l'événement load. Je remercie par avance tout aide fournie.


EDIT: J'ai déjà essayé les suggestions dans les réponses: en utilisant un point run at Différent, en utilisant DOMContentLoaded et en ajoutant à document.documentElement. Tous ont échoué car: 1 et 2 font que le script manque les premiers événements, et 3 renvoie la même TypeError que lorsque vous essayez d'ajouter à document.head.

Le script doit être inséré/exécuté lorsque document.readyState = loading Sinon il manquera les premiers événements nécessaires, mais pas si tôt au point de ne pas pouvoir ajouter des enfants à documentElementou head

Un exemple du code à l'intérieur de simpleuserscript.user.js:

var script = document.createElement("script");
script.textContent = "console.log('success')";
if(document.head) {
    document.head.appendChild(script);
} else if(document.documentElement) {
    document.documentElement.appendChild(script);
}

La console affichera TypeError: Cannot call method 'appendChild' of null

14
Shadow

Extension Chrome Scripts de contenu (exécutés à partir d'un manifest.json) qui s'exécutent à document_start, le tir se déclenche avant document.readyStateDoc a atteint interactive - qui est la première date à laquelle vous souhaitez commencer à jouer avec la plupart des éléments de page.

Cependant, vous pouvez injecter la plupart des <script> nœuds tout de suite si vous le souhaitez. Juste pas pour document.head ou document.body car ils n'existent pas encore.
Ajoutez à la place à documentElement. Par exemple:

var s = document.createElement ("script");
s.src = "http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js";
s.async = false;
document.documentElement.appendChild (s);

Ou

var s = document.createElement ("script");
s.src = chrome.extension.getURL ("MyPwnCode.js");
s.async = false;
document.documentElement.appendChild (s);

Si vous ajoutez ou modifiez d'autres éléments DOM , dans un script exécuté à document_start, attendez que l'événement DOMContentLoaded comme ceci:

document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);

function fireContentLoadedEvent () {
    console.log ("DOMContentLoaded");
    // PUT YOUR CODE HERE.
    //document.body.textContent = "Changed this!";
}
22
Brock Adams

Votre problème est que, lorsque vous utilisez "run_at": "document_start", le seul élément autorisé à exister dans le DOM est le <html> élément. Si vous voulez éviter les erreurs relatives au chargement de la page, comme essayer d'accéder à un élément qui n'a pas encore été créé, vous devrez soit:

  • Faites exécuter votre script à "document_idle" ou "document_end". Bien que "document_end" ne vous accorde toujours pas que tous les éléments et ressources de la page ont été entièrement chargés (mais le DOM a déjà été analysé), le "document_idle" Le mot clé vous donnera la certitude que le DOM a été analysé et que tous les éléments et ressources ont été chargés correctement avant l'exécution de votre script.

  • Ou, à la place, vous pouvez continuer à utiliser "document_start" envelopper votre code dans un "DOMContentLoaded" écouteur, qui le fera fonctionner lorsque le DOM aura complètement terminé le chargement et l'analyse, de la même manière que "document_idle". Voici un exemple:

    document.addEventListener("DOMContentLoaded", function() {
        // Run your code here...
    });
    
8
Marco Bonelli