用户脚本&Gresemonkey:调用网站的JavaScript函数

我正在为Firefox&;Chrome创建一个UserScript扩展,并尝试使用网站的JavaScript中的一些代码,例如:

function: myFunction(){
    return  Grooveshark.playNextSong();
}

问题是,当我测试此代码时,Grooveshark是空引用。

我知道还有其他人也这么做过:

see BetterGrooveshark

但我不知道为什么我的简单扩展不能调用Grooveshark的JavaScript函数。

我是否需要将我的脚本附加到文档才能正常工作?: document.document.body.appendChild(script);

Gresemonkey不是已经注入了我的扩展吗?有人能帮我澄清一下吗?

谢谢。


解决方案

背景

Gresemonkey不是已经注入了我的扩展吗?有人能帮我澄清一下吗?

Gresemonkey在sandbox中执行您的脚本,这是一个受限环境,无法直接访问页面中的JavaScript。早期版本的Gresemonkey直接将脚本注入页面,但这会带来严重的安全漏洞。在旧的模型中,脚本以浏览器Chrome的提升权限运行,这允许远程页面使用一些clever JavaScript访问Gresemonkey的内置函数。这很糟糕:

Gresemonkey脚本包含它们自己的GM_XMLHttpRequest对象,与普通的XMLHttRequest对象不同,该对象可以访问计算机上的任何本地文件或向任意站点发出任意请求,而不考虑通常应用于XMLHttpRequest的同源策略。(source)

当您现在从Gresemonkey脚本访问window对象时,您得到的是间接引用实际window的属性的wrapper object。此包装对象可以安全地修改,但具有important limitations。对实际窗口对象的访问由unsafeWindow(window.wrappedJSObject的缩写)提供。使用unsafeWindow会重新打开Gresemonkey的所有原始安全问题,在Chrome中不可用。应尽可能避免。

好消息:至少有两种方法可以安全地使用Gresemonkey的新安全模型。

脚本注入

既然Gresemonkey脚本可以安全地访问DOM,那么将inject a <script>标记到目标文档的<head>中就很简单了。创建如下函数:

function exec(fn) {
    var script = document.createElement('script');
    script.setAttribute("type", "application/javascript");
    script.textContent = '(' + fn + ')();';
    document.body.appendChild(script); // run the script
    document.body.removeChild(script); // clean up
}

使用简单:

exec(function() {
    return Grooveshark.playNextSong();
});

位置攻击

脚本注入在某些情况下可能过于夸张,特别是当您所需要的只是修改页面中的变量值或执行单个函数时。Location Hack利用javascript:URL访问文档内容中的代码。这非常类似于从Gresemonkey脚本中运行一个bookmarklet。

location.assign("javascript:Grooveshark.playNextSong();void(0)");

奖金脚本

下面是一个完整的Gresemonkey脚本,它演示了上述示例。您可以在此页面上运行它。

// ==UserScript==
// @name           Content Function Test
// @namespace      lwburk
// @include        http://stackoverflow.com/questions/5006460/userscripts-greasemonkey-calling-a-websites-javascript-functions
// ==/UserScript==

function exec(fn) {
    var script = document.createElement('script');
    script.setAttribute("type", "application/javascript");
    script.textContent = '(' + fn + ')();';
    document.body.appendChild(script); // run the script
    document.body.removeChild(script); // clean up
}

window.addEventListener("load", function() {
    // script injection
    exec(function() {
        // alerts true if you're registered with Stack Overflow
        alert('registered? ' + isRegistered);
    });
    // location hack
    location.assign("javascript:alert('registered? ' + isRegistered);void(0)");
}, false);

相关文章