函数未定义或权限被拒绝访问Greasemonkey中的对象

时间:2018-01-05 23:35:24

标签: javascript greasemonkey

我正在使用Greasemonkey脚本,该脚本将一个按钮注入聊天系统(Gitter),允许您发送默认消息。 (不要发送垃圾邮件。管理员可以发送行为准则等信息。)

我们假设我已经注入了一个按钮:

<button onclick='setTimeout( function() { showMsg() }, 10 );'>Send default message</button>

根据此问题的建议:Javascript: Function is defined, but Error says.. Function is not found ! (Strange)使用setTimeout()因为GM中定义的功能无法在主窗口中使用。

出于测试目的,该函数是一个简单的控制台日志:

function showMsg(){
    console.log('showMsg triggered');
}

问题是,这仍然会给我一个&#34; showMsg未定义&#34;。因此,按照前面提到的问题提供的其他adivse,我尝试了:

unsafeWindow.showMsg = function(){
    console.log('showMsg triggered');
}

但这让我回头:

  

错误:拒绝访问对象的权限

奇怪的是,似乎只有在创建div元素并使用innerHTML将按钮注入该div时才会出现这种情况。实际创建这样的按钮时:

var btn = document.createElement('button');
btn.onclick = function(){ showMsg(); };

它按预期工作。

可能有助于找到答案的其他一些信息:
Gitter聊天使用iframe作为聊天消息和消息输入字段。 iframe位于同一台服务器上,因此没有Same Original策略问题。

按钮被注入主体。以下是关于我如何尝试注射它们的两个例子。

(工作版)

var iframe, iframeDoc;
var chatContainer;
var chatInput, chatText;
var msgMenu;

$(document).ready(function() {
    iframe = document.getElementById('content-frame');

    iframe.onload = function(){
        iframeDoc = iframe.contentDocument || iframe.contentWindow.document;

        chatContainer = iframeDoc.getElementById('chat-container');
        chatInput = iframeDoc.getElementById('chat-input');
        chatText = iframeDoc.getElementById('chat-input-textarea');

        loadButton();
    }
});

function loadButton(){
    var div = document.createElement('div');
    var btn = document.createElement('button');
    var btnText = document.createTextNode('Send default message');

    // Lots of CSS here that ads absolute positioning
    // and makes the div float in the middle of the screen

    btn.onclick = function(){ showMsg(); };

    btn.appendChild(btnText);
    div.appendChild(btn);

    div.setAttribute("id", "msgMenu");

    document.body.appendChild(div);
    msgMenu = document.getElementById('msgMenu');
    return false;
}

function showMsg(){
    console.log('showMsg triggered');
}

将loadButton函数更改为此时,会导致上述问题:

function loadButton(){
    var div = document.createElement('div');

    div.innerHTML = "<button onclick='setTimeout( function() { showMsg() }, 10 );'>Send default message</button>";

    div.setAttribute("id", "msgMenu");

    document.body.appendChild(div);
    msgMenu = document.getElementById('msgMenu');
    return false;
}

1 个答案:

答案 0 :(得分:4)

您的用户脚本是“content script”,它在Greasemonkey扩展(或其他用户脚本管理器)的上下文中运行,并且可以访问Greasemonkey API。它被称为内容脚本,因为它可以访问网页的内容,即DOM。

从网页的HTML中调用的脚本是“页面脚本”,并在单独的上下文中运行。他们无法直接访问内容脚本的范围,包括您的用户脚本。

在两个示例中,showMsg()函数仅存在于内容脚本的范围内。在工作示例中,将匿名函数附加到按钮的onclick处理程序的代码在同一作用域中执行,其中showMsg()可见。对<button>节点附加了对匿名函数的引用,创建了一个closure,即使在主用户脚本执行完毕后也会使其保留在内存中。闭包还意味着函数可以访问定义函数时在范围内的相同对象,包括showMsg()(以及GM API对象)。

在第二个示例中,通过在页面脚本范围内评估其父innerHTML的{​​{1}}来创建该按钮。这意味着在单击处理程序附加到按钮时,<div>不可见,在调用处理程序时给出“未定义”错误。

问题不在于您使用showMsg()来注入按钮;是你用它来附加点击处理程序。此代码也可以使用innerHTML创建按钮,但是从内容脚本范围附加处理程序:

innerHTML

我无法准确解释您为什么会收到function loadButton(){ var div = document.createElement('div'); div.innerHTML = "<button id='myButton'>Send default message</button>"; document.body.appendChild(div); msgMenu = document.getElementById('msgMenu'); document.getElementById('myButton').addEventListener('click', showMsg); return false; } 的错误消息,但如果您使用的是Greasemonkey v4或更高版本,和/或Firefox v57或更高版本,则可能是由于最近的更改Firefox如何处理扩展。有关更多线索,请参阅MDNGreasemonkey blog

如果确实需要向网页本身添加功能,一种相对简单可靠的方法是将一个unsafeWindow节点注入DOM。请注意,插入的函数将无法访问usercript范围内的任何内容。 MDN上有more documentation描述了与页面脚本交互的更复杂的方法,但其中一些只适用于Firefox。