从 Chrome 上的 Greasemonkey 脚本将 JS 函数注入页面
Posted
技术标签:
【中文标题】从 Chrome 上的 Greasemonkey 脚本将 JS 函数注入页面【英文标题】:Injecting JS functions into the page from a Greasemonkey script on Chrome 【发布时间】:2011-01-19 04:14:18 【问题描述】:我有一个在 Firefox 和 Opera 中运行良好的 Greasemonkey 脚本。但是,我很难让它在 Chrome 中工作。问题是将一个函数注入到页面中,该函数可以由页面中的代码调用。以下是我目前正在做的事情:
首先,我得到一个对 Firefox 的 unsafeWindow 的帮助引用。这让我可以为 FF 和 Opera(还有 Chrome,我想)拥有相同的代码。
var uw = (this.unsafeWindow) ? this.unsafeWindow : window;
接下来,我将一个函数注入到页面中。它实际上只是一个非常薄的包装器,除了在我的 GM 脚本的上下文中调用相应的函数之外什么都不做:
uw.setConfigOption = function(newValue)
setTimeout(setConfigOption, 0, newValue);
然后,我的脚本中就有对应的函数:
setConfigOption = function(newValue)
// do something with it, e.g. store in localStorage
最后,我将一些 html 注入到页面中,并带有一个链接来调用该函数。
var p = document.createElement('p');
p.innerHTML = '<a href="javascript:setConfigOption(1)">set config option to 1</a>';
document.getElementById('injection-point').appendChild(p);
总结一下: 在 Firefox 中,当用户单击注入的链接时,它将在 unsafeWindow 上执行函数调用,然后触发超时,在我的 GM 脚本的上下文中调用相应的函数,然后执行实际处理。 (如果我在这里错了,请纠正我。)
在 Chrome 中,我只是收到“未捕获的 ReferenceError:未定义 setConfigOption”错误。实际上,在控制台中输入“window.setConfigOption”会产生“未定义”。在 Firebug 和 Opera 开发者控制台中,该功能就在那里。
也许还有另一种方法可以做到这一点,但我的一些函数是由页面上的 Flash 对象调用的,我认为这使得我有必要在页面上下文中拥有函数。
我快速浏览了 Greasemonkey wiki 上的 alternatives to unsafeWindow,但它们看起来都很丑。我在这里完全走错了路还是应该更仔细地研究这些?
解决方案:我关注了Max S.' advice,它现在可以在 Firefox 和 Chrome 中使用。因为我需要对页面可用的函数必须回调到常规函数中,所以我将整个脚本移至页面,即它完全封装在他称为“main()”的函数中。
为了让那个 hack 的额外丑陋更容易忍受,我现在至少可以放弃 unsafeWindow 和 WrappedJSObject 的使用。
我仍然没有设法从 Greasemonkey wiki 获得 content scope runner 工作。它应该做同样的事情并且它似乎执行得很好,但是我的函数永远无法被页面中的<a>
元素访问,例如。我还没弄清楚为什么会这样。
【问题讨论】:
【参考方案1】:在 Chrome 中与页面上运行的代码进行通信的唯一方法是通过 DOM,因此您必须使用一种技巧,例如在代码中插入 <script>
标记。请注意,如果您的脚本需要在页面上的所有其他内容之前运行,这可能会出现错误。
编辑:the Nice Alert extension 是这样做的:
function main ()
// ...
window.alert = function() /* ... */;
// ...
var script = document.createElement('script');
script.appendChild(document.createTextNode('('+ main +')();'));
(document.body || document.head || document.documentElement).appendChild(script);
【讨论】:
我尝试了“内容范围运行器”(如果我没记错的话,它就是这样做的),虽然我的脚本可以使用这些功能,但它们似乎不适用于页面(例如锚标记)。您有任何示例代码或可以链接到的任何内容吗?我不是很关心执行的顺序。当我设法从页面调用注入函数时,我可以解决这个问题。 :) 内容脚本执行顺序可以通过run_at
语句code.google.com/chrome/extensions/content_scripts.html定义
re run_at:不过,我不是在编写 Chrome 扩展程序(而且我不打算这样做,维护两个脚本需要做太多工作)。这只是一个 Greasemonkey 脚本,我试图使其足够兼容,以便 Chrome 可以将 GM 脚本“自动转换”为扩展。
虽然您的孤立示例对我有用,但我还没有在我的完整脚本中使用它。我想,与使用 unsafeWindow 时不同,我现在无法回调直接在我的脚本中的函数。我想我必须将所有内容都移到页面上下文中。我明天试试。
@NV: run_at=document_start
在将<script>
插入尚未加载的 DOM 时可能会遇到问题。 @hheimbuerger:是的,将所有代码包装到一个函数中,然后用脚本插入它。【参考方案2】:
我有这个:
contentscript.js:
function injectJs(link)
var scr = document.createElement('script');
scr.type="text/javascript";
scr.src=link;
document.getElementsByTagName('head')[0].appendChild(scr)
//document.body.appendChild(scr);
injectJs(chrome.extension.getURL('injected.js'));
injected.js :
function main()
alert('Hello World!');
main();
【讨论】:
这个太棒了!也是使用 jQuery 的人的快捷方式:function injectjs(link) $('<script type="text/javascript" src="'+link+'"/>').appendTo($('head'));
【参考方案3】:
其他答案要么强制您使用function expressions,导入external additional file 或使用long patched hack。
此答案将直接从您的源代码将 javascript 添加到页面中。它将使用 ECMAScript 6 (ES6) template literals 轻松地将多行 javascript 字符串放到页面上。
var script = document.createElement('script');
script.type = "text/javascript";
script.innerHTML = `
function test()
alert(1);
`;
document.getElementsByTagName('head')[0].appendChild(script);
请注意定义多行字符串开头和结尾的反引号``
。
【讨论】:
【参考方案4】:我快速浏览了 Greasemonkey wiki 上的 alternatives to unsafeWindow,但它们看起来都很丑。我在这里完全走错了路还是应该更仔细地研究这些?
你应该看看,因为它是唯一可用的选项。我更喜欢使用location hack。
myscript.user.js:
function myFunc()
alert('Hello World!');
location.href="javascript:(function()" + myFunc + ")()"
example.com/mypage.html
<script>
myFunc() // Hello World!
</script>
当然,这很丑。但它运作良好。
Max S. 提到的 Content Scope Runner 方法比 location hack 更好,因为它更容易调试。
【讨论】:
您能否发布一个示例,说明如何使用它来定义可由锚点或 Flash 对象调用的函数? 你是什么意思“通过锚”? 带有 标签,如我的示例代码中所示,例如。 (我认为如果这可行,它也应该来自 Flash 对象。) myscript.user.js:location.href="javascript:(function()window.setConfigOption=function() /* ... */ )()"
我想这也行,但我选择了 Max S。目前类似于内容范围运行器的方法。感谢您的帮助!以上是关于从 Chrome 上的 Greasemonkey 脚本将 JS 函数注入页面的主要内容,如果未能解决你的问题,请参考以下文章
UserScripts & Greasemonkey:调用网站的 JavaScript 函数
在 Firefox、Greasemonkey 脚本中使用时,console.log 不起作用