컨텐츠 스크립트를 사용하여 페이지 컨텍스트에 코드 삽입 플래시 플레이어와 함께 사용하고 싶습니다 (나중에 HTML5와

Chrome 확장 프로그램을 만드는 방법을 배우고 있습니다. 방금 YouTube 이벤트를 잡기 위해 하나를 개발하기 시작했습니다. YouTube 플래시 플레이어와 함께 사용하고 싶습니다 (나중에 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!");

문제는 콘솔이 “시작되었습니다!”라는 메시지를 표시 한다는 것 입니다. 하지만 ‘상태가 변경되었습니다!’ 는 없습니다 . YouTube 동영상을 재생 / 일시 중지 할 때

이 코드를 콘솔에 넣으면 작동했습니다. 내가 무엇을 잘못하고 있지?



답변

컨텐츠 스크립트는 “격리 된 세계”환경 에서 실행됩니다 . state()메소드를 페이지 자체 에 주입해야 합니다.

chrome.*스크립트 에서 API 중 하나를 사용하려면 다음 답변에 설명 된대로 특수한 이벤트 핸들러를 구현해야합니다. Chrome 확장 프로그램-Gmail의 원래 메시지 검색 .

그렇지 않으면 chrome.*API 를 사용할 필요가 없으면 <script>태그 를 추가하여 페이지에 모든 JS 코드를 삽입하는 것이 좋습니다 .

목차

  • 방법 1 : 다른 파일 삽입
  • 방법 2 : 임베디드 코드 삽입
  • 방법 2b : 함수 사용
  • 방법 3 : 인라인 이벤트 사용
  • 삽입 된 코드의 동적 값

방법 1 : 다른 파일 삽입

이것은 코드가 많을 때 가장 쉽고 가장 좋은 방법입니다. 확장명 내의 파일에 실제 JS 코드를 포함하십시오 (예 🙂 script.js. 그런 다음 콘텐츠 스크립트를 다음과 같이하십시오 ( Google Chome“애플리케이션 바로 가기”사용자 정의 Javascript 여기에 설명 )

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

참고 :이 방법을 사용하는 경우 주입 된 script.js파일을 "web_accessible_resources"섹션에 추가해야합니다 ( ). 그렇지 않으면 Chrome에서 스크립트로드 를 거부 하고 콘솔에 다음 오류가 표시됩니다.

chrome-extension : // [EXTENSIONID] /script.js의로드 거부 확장 프로그램 외부의 페이지에서로드하려면 리소스가 web_accessible_resources 매니페스트 키에 나열되어 있어야합니다.

방법 2 : 임베디드 코드 삽입

이 방법은 작은 코드를 빠르게 실행하려는 경우에 유용합니다. (또한 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();

참고 : 템플릿 리터럴 은 Chrome 41 이상에서만 지원됩니다. 확장 프로그램이 Chrome 40에서 작동하게하려면 다음을 사용하십시오.

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');

방법 2b : 함수 사용

큰 코드 덩어리의 경우 문자열을 인용하는 것이 가능하지 않습니다. 배열을 사용하는 대신 함수를 사용하고 문자열화할 수 있습니다.

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();

+문자열과 함수 의 연산자는 모든 객체를 문자열로 변환 하기 때문에이 방법이 작동 합니다. 코드를 두 번 이상 사용하려는 경우 코드 반복을 피하는 함수를 작성하는 것이 좋습니다. 구현은 다음과 같습니다.

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

참고 : 함수가 직렬화되므로 원래 범위와 모든 바인딩 된 속성이 손실됩니다!

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

방법 3 : 인라인 이벤트 사용

경우에 따라 <head>요소를 만들기 전에 일부 코드를 실행하는 등 일부 코드를 즉시 실행하려는 경우가 있습니다 . <script>태그를 삽입하여 수행 할 수 있습니다 textContent(방법 2 / 2b 참조).

대안 이지만 권장되지않지만 인라인 이벤트를 사용하는 것입니다. 페이지가 인라인 스크립트를 금지하는 컨텐츠 보안 정책을 정의하면 인라인 이벤트 리스너가 차단되므로 권장하지 않습니다. 반면, 확장에 의해 주입 된 인라인 스크립트는 여전히 실행됩니다. 인라인 이벤트를 계속 사용하려면 다음과 같이하십시오.

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');

참고 :이 메소드는 이벤트를 처리하는 다른 글로벌 이벤트 리스너가 없다고 가정합니다 reset. 있는 경우 다른 글로벌 이벤트 중 하나를 선택할 수도 있습니다. JavaScript 콘솔 (F12)을 열고을 입력 document.documentElement.on한 후 사용 가능한 이벤트를 선택하십시오.

삽입 된 코드의 동적 값

간혹 주입 된 함수에 임의의 변수를 전달해야합니다. 예를 들면 다음과 같습니다.

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

이 코드를 삽입하려면 변수를 인수로 익명 함수에 전달해야합니다. 올바르게 구현하십시오! 다음은 작동 하지 않습니다 .

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!

해결책은 JSON.stringify인수를 전달하기 전에 사용 하는 것입니다. 예:

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

변수가 많은 경우 JSON.stringify다음과 같이 가독성을 높이기 위해 한 번만 사용하는 것이 좋습니다 .

...
} + ')(' + JSON.stringify([arg1, arg2, arg3, arg4]) + ')';


답변

유일한 것 잃어버린 Rob W의 탁월한 답변에 숨겨진 것은 삽입 된 페이지 스크립트와 컨텐츠 스크립트 사이의 통신 방법입니다.

수신 측 (콘텐츠 스크립트 또는 삽입 된 페이지 스크립트)에서 이벤트 리스너를 추가하십시오.

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

이니시에이터 측 (콘텐츠 스크립트 또는 삽입 된 페이지 스크립트)에서 이벤트를 보내십시오.

var data = {
  allowedTypes: 'those supported by structured cloning, see the list below',
  inShort: 'no DOM elements or classes/functions',
};

document.dispatchEvent(new CustomEvent('yourCustomEvent', { detail: data }));

노트:

  • DOM 메시징은 구조적 복제 알고리즘을 사용하는데,이 알고리즘 은 기본 값 외에 일부 데이터 유형 만 전송할 수 있습니다 . 클래스 인스턴스, 함수 또는 DOM 요소를 보낼 수 없습니다.
  • Firefox에서 컨텐츠 스크립트의 객체 (예 : 기본 값이 아님)를 페이지 컨텍스트로 보내려면 cloneInto(내장 함수)를 사용하여 명시 적으로 대상에 오브젝트를 복제해야합니다 . 그렇지 않으면 보안 위반 오류로 실패합니다. .

    document.dispatchEvent(new CustomEvent('yourCustomEvent', {
      detail: cloneInto(data, document.defaultView),
    }));

답변

또한로드 된 스크립트의 순서 문제에 직면했습니다.로드 된 스크립트는 순차적으로로드 된 스크립트를 통해 해결되었습니다. 로딩은 Rob 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();
    }
}

사용 예는 다음과 같습니다.

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")
]);

사실, 나는 JS에 익숙하지 않으므로 더 나은 방법으로 나를 핑 (ping) 할 수 있습니다.


답변

Content script에서는 ‘onmessage’핸들러를 바인딩하는 헤드에 스크립트 태그를 추가합니다. 부스 콘텐츠 스크립트에서 onmessage 핸들러도 사용하므로 양방향 통신이 가능합니다.
크롬 문서

//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는 게시물 메시지 URL 리스너입니다.

//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");

이렇게하면 CS와 Real Dom간에 양방향 통신이 가능합니다. 예를 들어 webscoket 이벤트를 수신해야하거나 메모리 변수 또는 이벤트를 수신해야하는 경우 매우 유용합니다.


답변

페이지 컨텍스트에서 코드를 실행하고 반환 된 값을 가져 오기 위해 만든 유틸리티 함수를 사용할 수 있습니다.

이것은 함수를 문자열로 직렬화하고 웹 페이지에 주입하여 수행됩니다.

이 유틸리티는 GitHub에서 사용할 수 있습니다 .

사용 예-



// Some code that exists only in the page context -
window.someProperty = 'property';
function someFunction(name = 'test') {
    return new Promise(res => setTimeout(()=>res('resolved ' + name), 1200));
}
/////////////////

// Content script examples -

await runInPageContext(() => someProperty); // returns 'property'

await runInPageContext(() => someFunction()); // returns 'resolved test'

await runInPageContext(async (name) => someFunction(name), 'with name' ); // 'resolved with name'

await runInPageContext(async (...args) => someFunction(...args), 'with spread operator and rest parameters' ); // returns 'resolved with spread operator and rest parameters'

await runInPageContext({
    func: (name) => someFunction(name),
    args: ['with params object'],
    doc: document,
    timeout: 10000
} ); // returns 'resolved with params object'


답변

텍스트 대신 순수한 기능을 주입하려면 다음 방법을 사용할 수 있습니다.

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');

그리고 매개 변수를 전달할 수 있습니다 (불행히도 개체와 배열을 문자열로 지정할 수 없습니다). 다음과 같이 맨더에 추가하십시오.

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+")"; 


답변