使用内容脚本将代码插入到页面上下文中 [英] Insert code into the page context using a content script

查看:120
本文介绍了使用内容脚本将代码插入到页面上下文中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习如何制作Chrome扩展程序。我刚开始开发一个YouTube视频。我想将它用于YouTube Flash播放器(稍后我将尝试使其与HTML5兼容)。


$ b manifest.json

  {
name:MyExtension,
version:1.0,
description:抓住Youtube事件!,
权限:[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!);

问题是,控制台给了我Started!,但当我播放/暂停YouTube视频时,没有状态已更改!



当此代码放入控制台时, 。我在做什么错了?

解决方案

内容脚本在孤立的世界环境。您必须将 state()方法注入页面本身。



当您想要使用其中一个 chrome。* 脚本中的API,您必须实现一个特殊的事件处理程序,如本答案中所述: Chrome扩展程序 - 检索Gmail的原始邮件

我不建议使用 chrome。* API,我强烈建议通过添加< script> tag:

目录




  • 方法1 :注入另一个文件
  • 方法2:注入嵌入代码
  • 方法2b:使用函数
  • 方法3:使用内联事件

  • 注入代码中的动态值



方法1:注入另一个文件



这是最简单/最好的方法wh你有很多代码。将你的实际JS代码包含在你的扩展中的一个文件中,比如 script.js 。然后让您的内容脚本如下所示(这里解释: Google Chome应用程序快捷方式自定义Javascript ):

  var s = document.createElement('script'); 
// TODO:在manifest.json中将script.js添加到web_accessible_resources中
s.src = chrome.extension.getURL('script.js');
s.onload = function(){
this.remove();
};
(document.head || document.documentElement).appendChild(s);

注意:如果使用此方法,则注入脚本。必须将js 文件添加到 web_accessible_resources 部分例如)。如果您不这样做,Chrome会拒绝加载您的脚本,并在控制台中显示以下错误:


拒绝Chrome扩展的加载:// [EXTENSIONID] /script.js。必须在web_accessible_resources清单键中列出资源,才能通过扩展名外的页面加载资源。方法2:注入嵌入的代码

这个方法对你有用想要快速运行一小段代码。 (另请参阅:如何通过Chrome扩展禁用Facebook热键?)。

  var actualCode =`//代码在这里。 
//如果你想使用一个变量,使用$和花括号。
//例如,要使用固定的随机数:
var someFixedRandomValue = $ {Math.random()};
//注意:不要用这种方式插入不安全的变量,见下面的
//在注入的代码中的动态值
`;

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

注意:模板文字仅在Chrome 41及更高版本中受支持。如果您希望扩展程序在Chrome 40中运行,请使用:

  var actualCode = ['/ *代码在这里。例如:* /'+'alert(0);',
'//当心!这个数组必须被加入',
'//使用换行符。否则,缺少分号',
'//或单行注释(//)会将'',
'//代码----->']混淆。 \
');



方法2b:使用函数



对于大量的代码,引用字符串是不可行的。

  var实际代码='('+ function(){ 
//所有代码都在本地范围内执行
//例如,以下内容不会覆盖全局`alert`方法
var alert = null;
//要覆盖一个全局变量,前缀`window`:
window.alert = null;
} +')();';
var script = document.createElement('script');
script.textContent = actualCode;
(document.head || document.documentElement).appendChild(script);
script.remove();

这种方法可行,因为 + 运算符字符串和函数将所有对象转换为字符串。如果您打算多次使用该代码,那么创建一个避免代码重复的函数是明智的。一个实现可能如下所示:

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

注意:由于该函数是序列化的,原始范围和所有绑定属性都会丢失! p>

  var scriptToInject = function(){
console.log(typeof scriptToInject);
};
injectScript(scriptToInject);
//控制台输出:undefined



方法3:使用内联事件 h2>

有时,您想要立即运行一些代码,例如在< head> 元素创建之前运行一些代码。这可以通过在 textContent 中插入< script> 标签来完成(参见方法2 / 2b) p>

另一种方法是,但不推荐使用内联事件。不建议这样做,因为如果页面定义禁止内联脚本的内容安全策略,则内联事件侦听器将被阻止。另一方面,由扩展注入的内联脚本仍然运行。
如果你仍然想要使用内联事件,那么这就是:

  var actualCode ='//一些代码示例\\\
'+
'console.log(document.documentElement.outerHTML);';

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

注意:此方法假定没有其他全局事件侦听器处理重置事件。如果有,您还可以选择其他全球事件之一。只需打开JavaScript控制台(F12),键入 document.documentElement.on ,然后选择可用的事件。



< h2>注入代码中的动态值



有时,您需要将任意变量传递给注入函数。例如:

  var GREETING =我是; 
var NAME =Rob;
var scriptToInject = function(){
alert(GREETING + NAME);
};

要注入此代码,您需要将变量作为参数传递给匿名函数。一定要正确实施!

  var scriptToInject = function(GREETING,NAME){... 

};
var actualCode ='('+ scriptToInject +')('+ GREETING +','+ NAME')';
//前面的内容适用于数字和布尔值,但不适用于字符串。
//查看原因,查看结果字符串:
var actualCode =(函数(GREETING,NAME){...})(Hi I'm,Rob);
// ^^^^^^ ^^^没有字符串文字!

解决方案是使用 JSON.stringify 。例子:

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

如果您有很多变量,那么使用 JSON.stringify 一次,以提高可读性,如下所示:

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


I'm learning how to create Chrome extensions. I just started developing one to catch YouTube events. I want to use it with YouTube flash player (later I will try to make it compatible with 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!");

The problem is that the console gives me the "Started!", but there is no "State Changed!" when I play/pause YouTube videos.

When this code is put in the console, it worked. What am I doing wrong?

解决方案

Content scripts are executed in an "isolated world" environment. You have to inject your state() method into the page itself.

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.

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:

Table of contents

  • Method 1: Inject another file
  • Method 2: Inject embedded code
  • Method 2b: Using a function
  • Method 3: Using an inline event
  • Dynamic values in the injected code

Method 1: Inject another file

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. Then let your content script be as follows (explained here: 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.extension.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). If you do not, Chrome will refuse to load your script and display the following error in the console:

Denying load of 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.

Method 2: Inject embedded code

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?).

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. If you want the extension to work in Chrome 40-, use:

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

Method 2b: Using a function

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

Sometimes, you want to run some code immediately, e.g. to run some code before the <head> element is created. This can be done by inserting a <script> tag with textContent (see method 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 \n' + 
                 '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. 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.

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)";
//                                                 ^^^^^^ ^^^ No string literals!

The solution is to use JSON.stringify before passing the argument. Example:

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

If you have many variables, it's worthwhile to use JSON.stringify once, to improve readability, as follows:

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

这篇关于使用内容脚本将代码插入到页面上下文中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆