47

我想制作一个 Chrome 扩展程序,它在内部提供一个新对象window。当在加载了扩展程序的浏览器中查看网页时,我希望window.mything通过 Javascript 可用。该window.mything对象将具有一些我将在扩展中定义的函数,并且console.log当在启用了扩展的浏览器中查看页面时,这些函数应该可以从任何 Javascript 文件中调用。

我能够通过使用内容脚本成功地将 Javascript 文件注入页面:

var s = document.createElement("script"); 
s.src = chrome.extension.getURL("mything.js");
document.getElementsByTagName("head")[0].appendChild(s);

mything.js 看起来像这样:

window.mything = {thing: true};
console.log(window);

每当页面加载时,我都会看到整个window对象,就像我期望它在控制台中一样。但是,我无法window.mything从控制台与对象交互。似乎注入的脚本没有真正修改全局window对象。

如何window从 Chrome 扩展中修改全局对象?

4

7 回答 7

56

你不能,不能直接。从内容脚本文档

但是,内容脚本有一些限制。他们不能:

  • 使用 chrome.* API(chrome.extension 的部分除外)
  • 使用由其扩展页面定义的变量或函数
  • 使用由网页或其他内容脚本定义的变量或函数

(重点补充)

window内容脚本看到的对象window与页面看到的对象不同。

window.postMessage但是,您可以使用该方法通过 DOM 传递消息。您的页面和内容脚本都会监听消息事件,并且每当您window.postMessage从其中一个地方调用时,另一个地方都会收到它。“内容脚本”文档页面上有一个示例。

编辑:您可以通过从内容脚本注入脚本来向页面添加一些方法。尽管不使用类似的东西,它仍然无法与扩展的其余部分进行通信postMessage,但您至少可以在页面的window

var elt = document.createElement("script");
elt.innerHTML = "window.foo = {bar:function(){/*whatever*/}};"
document.head.appendChild(elt);
于 2012-09-12T21:08:39.803 回答
19

经过数小时尝试不同的尝试并面临 CORS 等安全问题后,我找到了window在和. 您需要对每种策略使用不同的策略:ChromeFirefoxSafari

铬合金

  1. 将您的脚本添加到content_scripts.
  2. 在您的脚本文件中,将 a 附加script到页面并使其内联运行您的自定义代码。像这样:
;(function() {
  function script() {
    // your main code here
    window.foo = 'bar'
  }

  function inject(fn) {
    const script = document.createElement('script')
    script.text = `(${fn.toString()})();`
    document.documentElement.appendChild(script)
  }

  inject(script)
})()

火狐

Content-Security-Policy在 Firefox 上,由于错误,上述解决方案不起作用。但是以下解决方法目前正在起作用,至少目前是这样:

  1. 将 2 个脚本添加到content_scripts,例如inject.jsscript.js
  2. inject脚本将获取文件的完整绝对urlscript.js并加载它:
;(function() {
  const b = typeof browser !== 'undefined' ? browser : chrome

  const script = document.createElement('script')
  script.src = b.runtime.getURL('script.js')
  document.documentElement.appendChild(script)
})()
  1. 您的script.js将包含您的主要代码:
;(function() {
  // your main code here
  window.foo = 'bar'
})()

苹果浏览器

它与 Firefox 非常相似。

  1. 创建 2 个 javascript 文件,例如inject.jsscript.js
  2. inject脚本将获取文件的完整绝对urlscript.js并加载它:
;(function() {
  const script = document.createElement('script')
  script.src = safari.extension.baseURI + 'script.js'
  document.documentElement.appendChild(script)
})()
  1. 您的script.js将包含您的主要代码:
;(function() {
  // your main code here
  window.foo = 'bar'
})()

源代码

在此处查看完整代码:https ://github.com/brunolemos/simplified-twitter

于 2019-08-02T00:36:17.703 回答
11

正如其他人指出的那样,上下文脚本不会在与页面相同的上下文中运行,因此,要访问正确的window,您需要将代码注入页面。

这是我的看法:

function codeToInject() {
    // Do here whatever your script requires. For example:
    window.foo = "bar";
}

function embed(fn) {
    const script = document.createElement("script");
    script.text = `(${fn.toString()})();`;
    document.documentElement.appendChild(script);
}

embed(codeToInject);

清洁且易于使用。无论您需要在页面上下文中运行什么,都将其放入codeToInject()(您可以随意称呼它)。该embed()函数负责打包您的函数并将其发送到页面中运行。

embed()函数的作用是script在页面中创建一个标签并将该函数codeToInject()作为IIFE嵌入其中。浏览器将在新标签附加到文档后立即执行script它,并且您注入的代码将按预期在页面上下文中运行。

于 2018-10-12T01:54:51.253 回答
8

chrome 扩展程序content_script在其自己的上下文中运行,该上下文与窗口分开。您可以将脚本注入到页面中,以便它在与页面窗口相同的上下文中运行,如下所示:Chrome 扩展 - 从网页中检索全局变量

我能够调用窗口对象上的方法并通过将 a 添加script.js到页面的 DOM 来修改窗口属性:

var s = document.createElement('script');
s.src = chrome.extension.getURL('script.js');
(document.head||document.documentElement).appendChild(s);
s.onload = function() {
    s.remove();
};

并在注入的脚本文件中创建自定义事件侦听器:

document.addEventListener('_my_custom_event', function(e) {
  // do whatever you'd like! Like access the window obj
  window.myData = e.detail.my_event_data;
})

并在 content_script 中调度该事件:

var foo = 'bar'
document.dispatchEvent(new CustomEvent('_save_OG_Editor', {
  'detail': {
    'my_event_data': foo
  }
}))

或相反亦然; 在扩展程序的 content_script 中分派事件script.js并监听它们(如上面的链接所示)。

只需确保在扩展文件中添加注入的脚本,并将脚本文件的路径添加到清单中,"web_accessible_resources"否则会出错。

希望对某人有所帮助\ (•◡•) /

于 2018-01-25T06:50:25.717 回答
6

我一直在玩这个。我发现我可以通过将我的 javascript 包装到 window.location= 调用中来与浏览器的窗口对象进行交互。

var myInjectedJs = "window.foo='This exists in the \'real\' window object';"
window.location = "javascript:" + myInjectedJs;

var myInjectedJs2 = "window.bar='So does this.';"
window.location = "javascript:" + myInjectedJs2;

它有效,但仅适用于设置的 window.location 的最后一个实例。如果您访问文档的窗口对象,它将有一个变量“bar”但没有“foo”

于 2016-04-12T01:40:24.830 回答
0

感谢这里的其他答案,这就是我正在使用的:

((source)=>{
  const script = document.createElement("script");
  script.text = `(${source.toString()})();`;
  document.documentElement.appendChild(script);
})(function (){

  // Your code here
  // ...

})

效果很好,没有问题。

于 2020-10-02T15:29:12.680 回答
-1

内容脚本可以调用window可以用来改变window对象的方法。这比<script>标签注入更容易,即使在<head><body>还没有被解析时(例如使用 时run_at: document_start)也能工作。

// In Content Script
window.addEventListener('load', loadEvent => {
    let window = loadEvent.currentTarget;
    window.document.title='You changed me!';
});
于 2017-10-23T11:54:22.897 回答