Wstaw kod do kontekstu strony za pomocą skryptu zawartości

Uczę się jak tworzyć rozszerzenia Chrome. Właśnie zacząłem opracowywać jeden, aby złapać wydarzenia na YouTube. Chcę go używać z YouTube flash player (później postaram się, aby był kompatybilny z HTML5).

Manifest.json:

{
    "name": "MyExtension",
    "version": "1.0",
    "description": "Gotta catch Youtube events!",
    "permissions": ["tabs", "http://*/*"],
    "content_scripts" : [{
        "matches" : [ "www.youtube.com/*"],
        "js" : ["myScript.js"]
    }]
}

MyScript.js:

function state() { console.log("State Changed!"); }
var player = document.getElementById("movie_player");
player.addEventListener("onStateChange", "state");
console.log("Started!");

Problem polega na tym, że konsola daje mi "rozpoczęty!", ale nie ma " Stan się zmienił!" kiedy odtwarzam / wstrzymuję filmy na YouTube.

Gdy ten kod zostanie umieszczony w konsoli, zadziałało. Co robię źle?

Author: Makyen, 2012-03-01

5 answers

Skrypty zawartości są wykonywane w środowisku "izolowanego świata" . Musisz wprowadzić swoją metodę state() do samej strony.

Jeśli chcesz użyć jednego z API chrome.* w skrypcie, musisz zaimplementować specjalną obsługę zdarzeń, jak opisano w tej odpowiedzi: rozszerzenie Chrome - pobieranie oryginalnej wiadomości Gmaila.

W przeciwnym razie, jeśli nie musisz używać chrome.* API, zdecydowanie polecam wstrzyknąć cały kod JS na stronie poprzez dodanie <script> tag:

Spis treści

  • Metoda 1: Inject another file
  • Metoda 2: wstrzyknięcie wbudowanego kodu
  • metoda 2b: Używanie funkcji
  • Metoda 3: użycie zdarzenia inline
  • wartości dynamiczne w kodzie

Metoda 1: Inject another file

jest to najprostsza/najlepsza metoda, gdy masz dużo kodu. Dołącz swój rzeczywisty kod JS do pliku w swoim rozszerzeniu, powiedzmy script.js. Następnie pozwól swoim treściom skrypt będzie następujący (wyjaśnione tutaj: Google Chome" skrót aplikacji " Niestandardowy Javascript):

var s = document.createElement('script');
// TODO: add "script.js" to web_accessible_resources in manifest.json
s.src = chrome.extension.getURL('script.js');
s.onload = function() {
    this.remove();
};
(document.head || document.documentElement).appendChild(s);

Uwaga: w przypadku użycia tej metody należy dodać plik script.js do "web_accessible_resources" sekcja (przykład ). Jeśli tego nie zrobisz, Chrome odmówi załadowania skryptu i wyświetli następujący błąd w konsoli:

Odmowa załadowania chrome-extension://[EXTENSIONID]/script.js. Zasoby muszą być wymienione w klucz manifestu web_accessible_resources w celu załadowania stron spoza rozszerzenia.

Metoda 2: wstrzyknięcie wbudowanego kodu

Ta metoda jest przydatna, gdy chcesz szybko uruchomić mały fragment kodu. (Zobacz także: jak wyłączyć skróty klawiszowe facebook z rozszerzeniem Chrome?).

var actualCode = `// Code here.
// If you want to use a variable, use $ and curly braces.
// For example, to use a fixed random number:
var someFixedRandomValue = ${ Math.random() };
// NOTE: Do not insert unsafe variables in this way, see below
// at "Dynamic values in the injected code"
`;

var script = document.createElement('script');
script.textContent = actualCode;
(document.head||document.documentElement).appendChild(script);
script.remove();

Uwaga: literały szablonów {[28] } są obsługiwane tylko w Chrome 41 i nowszych wersjach. Jeśli chcesz, aby rozszerzenie działało w Chrome 40-, użyj:

var actualCode = ['/* Code here. Example: */' + 'alert(0);',
                  '// Beware! This array have to be joined',
                  '// using a newline. Otherwise, missing semicolons',
                  '// or single-line comments (//) will mess up your',
                  '// code ----->'].join('\n');

Metoda 2b: Korzystanie z funkcji

Dla dużej części kodu cytowanie ciągu znaków nie jest możliwe. Zamiast używać tablicy, można użyć funkcji i stringified:

var actualCode = '(' + function() {
    // All code is executed in a local scope.
    // For example, the following does NOT overwrite the global `alert` method
    var alert = null;
    // To overwrite a global variable, prefix `window`:
    window.alert = null;
} + ')();';
var script = document.createElement('script');
script.textContent = actualCode;
(document.head||document.documentElement).appendChild(script);
script.remove();

Ta metoda działa, ponieważ operator + na łańcuchach znaków i funkcji konwertuje wszystkie obiekty na łańcuch znaków. Jeśli zamierzasz używać kodu więcej niż raz, dobrze jest utworzyć funkcję, aby uniknąć powtarzania kodu. Implementacja może wyglądać następująco:

function injectScript(func) {
    var actualCode = '(' + func + ')();'
    ...
}
injectScript(function() {
   alert("Injected script");
});

Uwaga: Ponieważ funkcja jest serializowana, oryginał zakres, a wszystkie związane właściwości są utracone!

var scriptToInject = function() {
    console.log(typeof scriptToInject);
};
injectScript(scriptToInject);
// Console output:  "undefined"

Metoda 3: użycie zdarzenia inline

Czasami chcesz uruchomić jakiś kod natychmiast, np. uruchomić jakiś kod przed utworzeniem elementu <head>. Można to zrobić wstawiając znacznik <script> z textContent (patrz Metoda 2/2B).

Alternatywą, ale niezalecaną jest użycie zdarzeń inline. Nie jest to zalecane, ponieważ jeśli strona definiuje politykę bezpieczeństwa treści, która zabrania skryptów inline, to inline słuchacze zdarzeń są zablokowani. Z drugiej strony Skrypty inline wstrzykiwane przez rozszerzenie nadal działają. Jeśli nadal chcesz używać zdarzeń inline, oto jak:]}

var actualCode = '// Some code example \n' + 
                 'console.log(document.documentElement.outerHTML);';

document.documentElement.setAttribute('onreset', actualCode);
document.documentElement.dispatchEvent(new CustomEvent('reset'));
document.documentElement.removeAttribute('onreset');

Uwaga: ta metoda zakłada, że nie ma innych globalnych detektorów zdarzeń, które obsługują Zdarzenie reset. Jeśli tak, możesz również wybrać jedno z innych wydarzeń globalnych. Po prostu otwórz konsolę JavaScript (F12), wpisz document.documentElement.on i wybierz dostępne zdarzenia.

Wartości dynamiczne we wstrzykiwanym kod

Od czasu do czasu trzeba przekazać dowolną zmienną do wstrzykiwanej funkcji. Na przykład:

var GREETING = "Hi, I'm ";
var NAME = "Rob";
var scriptToInject = function() {
    alert(GREETING + NAME);
};

Aby wprowadzić ten kod, musisz przekazać zmienne jako argumenty do funkcji anonimowej. Pamiętaj, aby wdrożyć go poprawnie! Następujące będą Nie działać:

var scriptToInject = function (GREETING, NAME) { ... };
var actualCode = '(' + scriptToInject + ')(' + GREETING + ',' + NAME ')';
// The previous will work for numbers and booleans, but not strings.
// To see why, have a look at the resulting string:
var actualCode = "(function(GREETING, NAME) {...})(Hi I'm,Rob)";
//                                                 ^^^^^^ ^^^ No string literals!

Rozwiązaniem jest użycie JSON.stringify przed przekazaniem argumentu. Przykład:

var actualCode = '(' + function(greeting, name) { ...
} + ')(' + JSON.stringify(GREETING) + ',' + JSON.stringify(NAME) + ')';

Jeśli masz wiele zmiennych, warto użyć JSON.stringify raz, aby poprawić czytelność, jak następuje:

...
} + ')(' + JSON.stringify([arg1, arg2, arg3, arg4]) + ')';
 739
Author: Rob W,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-15 17:29:11

Jedyną rzeczą Brak ukrytą przed doskonałą odpowiedzią wiersza W jest sposób wywołania skryptu z iniekcji do skryptu zawartości i odwrotnie (zwłaszcza jeśli masz obiekty, których nie można stringować).

W skrypcie injected lub content dodaj detektor zdarzeń:

document.addEventListener('yourCustomEvent', function (e)
{
  var data=e.detail;
  console.log("received "+data);
});

Po drugiej stronie (content lub injected script) wywołanie zdarzenia:

var data="anything";

// updated: this works with Chrome 30:
var evt=document.createEvent("CustomEvent");
evt.initCustomEvent("yourCustomEvent", true, true, data);
document.dispatchEvent(evt);

// the following stopped working in Chrome 30 (Windows), detail was 
// not received in the listener:
// document.dispatchEvent(new CustomEvent('yourCustomEvent', { detail: data }));
 45
Author: laktak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-06 15:42:16

Miałem również do czynienia z problemem porządkowania załadowanych skryptów, który został rozwiązany poprzez sekwencyjne Ładowanie skryptów. Ładowanie jest oparte na odpowiedzi Roba W .

function scriptFromFile(file) {
    var script = document.createElement("script");
    script.src = chrome.extension.getURL(file);
    return script;
}

function scriptFromSource(source) {
    var script = document.createElement("script");
    script.textContent = source;
    return script;
}

function inject(scripts) {
    if (scripts.length === 0)
        return;
    var otherScripts = scripts.slice(1);
    var script = scripts[0];
    var onload = function() {
        script.parentNode.removeChild(script);
        inject(otherScripts);
    };
    if (script.src != "") {
        script.onload = onload;
        document.head.appendChild(script);
    } else {
        document.head.appendChild(script);
        onload();
    }
}

Przykład użycia to:

var formulaImageUrl = chrome.extension.getURL("formula.png");
var codeImageUrl = chrome.extension.getURL("code.png");

inject([
    scriptFromSource("var formulaImageUrl = '" + formulaImageUrl + "';"),
    scriptFromSource("var codeImageUrl = '" + codeImageUrl + "';"),
    scriptFromFile("EqEditor/eq_editor-lite-17.js"),
    scriptFromFile("EqEditor/eq_config.js"),
    scriptFromFile("highlight/highlight.pack.js"),
    scriptFromFile("injected.js")
]);
Właściwie, jestem trochę nowy w JS, więc nie krępuj się, aby ping mnie do lepszych sposobów.
 8
Author: Dmitry Ginzburg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-18 01:11:13

W Content script, dodaję tag script do głowy, który wiąże handler 'onmessage' , wewnątrz Handlera którego używam , eval do wykonania kodu. W Booth content script używam również onmessage handler, więc dostaję dwukierunkową komunikację. Chrome Docs

//Content Script

var pmsgUrl = chrome.extension.getURL('pmListener.js');
$("head").first().append("<script src='"+pmsgUrl+"' type='text/javascript'></script>");


//Listening to messages from DOM
window.addEventListener("message", function(event) {
  console.log('CS :: message in from DOM', event);
  if(event.data.hasOwnProperty('cmdClient')) {
    var obj = JSON.parse(event.data.cmdClient);
    DoSomthingInContentScript(obj);
 }
});

PmListener.js is a post message URL listener

//pmListener.js

//Listen to messages from Content Script and Execute Them
window.addEventListener("message", function (msg) {
  console.log("im in REAL DOM");
  if (msg.data.cmnd) {
    eval(msg.data.cmnd);
  }
});

console.log("injected To Real Dom");

W ten sposób mogę mieć dwukierunkową komunikację pomiędzy CS a Real Dom. To bardzo przydatne, na przykład, jeśli chcesz słuchać wydarzeń webscoket , lub do każdego w pamięci zmienne lub zdarzenia.

 6
Author: doron aviguy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-11 08:29:11

Jeśli chcesz wprowadzić czystą funkcję, zamiast tekstu, możesz użyć tej metody:

function inject(){
    document.body.style.backgroundColor = 'blue';
}

// this includes the function as text and the barentheses make it run itself.
var actualCode = "("+inject+")()"; 

document.documentElement.setAttribute('onreset', actualCode);
document.documentElement.dispatchEvent(new CustomEvent('reset'));
document.documentElement.removeAttribute('onreset');

I można przekazać parametry (niestety nie można przeciągać żadnych obiektów i tablic) do funkcji. Dodajcie to do gołych, TAK:

function inject(color){
    document.body.style.backgroundColor = color;
}

// this includes the function as text and the barentheses make it run itself.
var color = 'yellow';
var actualCode = "("+inject+")("+color+")"; 
 0
Author: Tarmo Saluste,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-05 11:31:55