Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
316 views
in Technique[技术] by (71.8m points)

javascript - 使用内容脚本将代码插入页面上下文(Insert code into the page context using a content script)

I'm learning how to create Chrome extensions.

(我正在学习如何创建Chrome扩展程序。)

I just started developing one to catch YouTube events.

(我刚刚开始开发一个捕捉YouTube事件的工具。)

I want to use it with YouTube flash player (later I will try to make it compatible with HTML5).

(我想将其与YouTube Flash Player结合使用(稍后,我将尝试使其与HTML5兼容)。)

manifest.json:

(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:

(myScript.js:)

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

The problem is that the console gives me the "Started!"

(问题在于控制台为我提供了“开始!”)

, but there is no "State Changed!"

(,但没有“状态已更改!”)

when I play/pause YouTube videos.

(当我播放/暂停YouTube视频时。)

When this code is put in the console, it worked.

(将此代码放入控制台后,它就可以工作。)

What am I doing wrong?

(我究竟做错了什么?)

  ask by André Alves translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Content scripts are executed in an "isolated world" environment .

(内容脚本在“隔离的世界”环境中执行。)

You have to inject your state() method into the page itself.

(您必须将state()方法注入页面本身。)

When you want to use one of the chrome.* APIs in the script, you have to implement a special event handler, as described in this answer: Chrome extension - retrieving Gmail's original message .

(如果要在脚本中使用chrome.* API之一,则必须实现特殊的事件处理程序,如以下答案中所述: Chrome扩展程序-检索Gmail的原始消息 。)

Otherwise, if you don't have to use chrome.* APIs, I strongly recommend to inject all of your JS code in the page via adding a <script> tag:

(否则,如果您不必使用chrome.* API,我强烈建议通过添加<script>标签将所有JS代码注入页面中:)

Table of contents(目录)

  • Method 1: Inject another file

    (方法1:注入另一个文件)

  • Method 2: Inject embedded code

    (方法2:注入嵌入式代码)

  • Method 2b: Using a function

    (方法2b:使用一个函数)

  • Method 3: Using an inline event

    (方法3:使用一个内联事件)

  • Dynamic values in the injected code

    (注入代码中的动态值)

Method 1: Inject another file(方法1:注入另一个文件)

This is the easiest/best method when you have lots of code.

(当您有很多代码时,这是最简单/最佳的方法。)

Include your actual JS code in a file within your extension, say script.js .

(将实际的JS代码包括在扩展名内的文件中,例如script.js 。)

Then let your content script be as follows (explained here: Google Chome “Application Shortcut” Custom Javascript ):

(然后,让您的内容脚本如下所示(在此处进行说明: Google Chome“ Application Shortcut” Custom 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);

Note: If you use this method, the injected script.js file has to be added to the "web_accessible_resources" section ( example ).

(注意:如果使用此方法,则必须将注入的script.js文件添加到"web_accessible_resources"部分示例 )。)

If you do not, Chrome will refuse to load your script and display the following error in the console:

(如果不这样做,Chrome会拒绝加载脚本并在控制台中显示以下错误:)

Denying load of chrome-extension://[EXTENSIONID]/script.js.

(拒绝加载chrome-extension:// [EXTENSIONID] /script.js。)

Resources must be listed in the web_accessible_resources manifest key in order to be loaded by pages outside the extension.

(必须在web_accessible_resources清单键中列出资源,以便由扩展之外的页面加载。)

Method 2: Inject embedded code(方法2:注入嵌入式代码)

This method is useful when you want to quickly run a small piece of code.

(当您想快速运行一小段代码时,此方法很有用。)

(See also: How to disable facebook hotkeys with Chrome extension? ).

((另请参见: 如何使用Chrome扩展程序禁用Facebook热键? )。)

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

Note: template literals are only supported in Chrome 41 and above.

(注意: 模板文字仅在Chrome 41及更高版本中受支持。)

If you want the extension to work in Chrome 40-, use:

(如果您希望扩展程序在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('
');

Method 2b: Using a function(方法2b:使用一个函数)

For a big chunk of code, quoting the string is not feasible.

(对于一大段代码,引用字符串是不可行的。)

Instead of using an array, a function can be used, and 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();

This method works, because the + operator on strings and a function converts all objects to a string.

(此方法有效,因为字符串和函数上的+运算符会将所有对象转换为字符串。)

If you intend on using the code more than once, it's wise to create a function to avoid code repetition.

(如果您打算多次使用该代码,则明智的做法是创建一个避免代码重复的函数。)

An implementation might look like:

(一个实现可能看起来像:)

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

Note: Since the function is serialized, the original scope, and all bound properties are lost!

(注意:由于该函数已序列化,因此原始作用域和所有绑定的属性都将丢失!)

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

Method 3: Using an inline event(方法3:使用一个内联事件)

Sometimes, you want to run some code immediately, eg to run some code before the <head> element is created.

(有时,您想立即运行一些代码,例如在创建<head>元素之前运行一些代码。)

This can be done by inserting a <script> tag with textContent (see method 2/2b).

(这可以通过插入来完成<script>与标签textContent (参见方法2 / 2B)。)

An alternative, but not recommended is to use inline events.

(一种替代方法, 但不建议使用内联事件。)

It is not recommended because if the page defines a Content Security policy that forbids inline scripts, then inline event listeners are blocked.

(不建议这样做,因为如果页面定义了禁止内联脚本的内容安全策略,则内联事件侦听器将被阻止。)

Inline scripts injected by the extension, on the other hand, still run.

(另一方面,由扩展名注入的内联脚本仍在运行。)

If you still want to use inline events, this is how:

(如果您仍想使用内联事件,则可以这样:)

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

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

Note: This method assumes that there are no other global event listeners that handle the reset event.

(注意:此方法假定没有其他全局事件侦听器来处理reset事件。)

If there is, you can also pick one of the other global events.

(如果存在,您还可以选择其他全局事件之一。)

Just open the JavaScript console (F12), type document.documentElement.on , and pick on of the available events.

(只需打开JavaScript控制台(F12),键入document.documentElement.on ,然后选择可用事件。)

Dynamic values in the injected code(注入代码中的动态值)

Occasionally, you need to pass an arbitrary variable to the injected function.

(有时,您需要将任意变量传递给注入的函数。)

For example:

(例如:)

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

To inject this code, you need to pass the variables as arguments to the anonymous function.

(要注入此代码,您需要将变量作为参数传递给匿名函数。)

Be sure to implement it correctly!

(确保正确实施!)

The following will not work:

(以下将无法正常工作:)

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...