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

2024-03-25

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

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

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

根据这个问题的建议:Javascript:函数已定义,但错误提示..找不到函数! (奇怪的) https://stackoverflow.com/questions/5919781/javascript-function-is-defined-but-error-says-function-is-not-found-stran to use setTimeout()因为GM中定义的函数在主窗口中是不可用的。

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

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

问题是这仍然返回给我“showMsg 未定义”。因此,按照前面提到的问题中提供的其他建议,我尝试了:

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

然而,这让我返回:

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

奇怪的是,似乎这只在创建 div 元素并使用时才会发生innerHTML将按钮注入该 div 中。当实际创建一个这样的按钮时:

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

它按预期工作。

一些可能有助于找到答案的附加信息:
Gitter 聊天使用 iframe 来显示聊天消息和消息输入字段。 iframe 位于同一服务器上,因此不存在相同原始策略问题。

按钮被注入到主体中。以下是我如何尝试注入它们的两个示例。

(工作版本)

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;
}

您的用户脚本是“内容脚本 https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Content_scripts”,它在 Greasemonkey 扩展(或其他用户脚本管理器)的上下文中运行,并且可以访问 Greasemonkey API。它被称为内容脚本,因为它可以访问网页的内容,即 DOM。

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

在你的两个例子中,showMsg()函数仅存在于内容脚本的范围内。在工作示例中,将匿名函数附加到按钮的代码onclick处理程序在同一范围内执行,其中showMsg()是可见的。对匿名函数的引用附在<button>节点,创建一个closure https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures这会导致即使在主用户脚本执行完毕后它仍保留在内存中。闭包还意味着该函数可以访问定义该函数时范围内的相同对象,包括showMsg()(以及 GM API 对象)。

在第二个示例中,按钮是通过评估innerHTML其父级的<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;
}

我无法准确解释为什么您会收到错误消息unsafeWindow,但如果您使用的是 Greasemonkey v4 或更高版本,和/或 Firefox v57 或更高版本,则可能是由于 Firefox 处理扩展的方式最近发生了变化。看MDN https://developer.mozilla.org/en-US/docs/Archive/Add-ons/Add-on_SDK/Guides/Content_Scripts/Interacting_with_page_scripts油猴博客 https://www.greasespot.net/2017/09/greasemonkey-4-for-script-authors.html以获得更多线索。

如果确实需要给网页本身添加功能,一个比较简单可靠的方法就是注入一个<script>节点到 DOM 中。请注意,插入的函数将无法访问用户脚本范围内的任何内容。有更多文档 https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Content_scripts#Sharing_objects_with_page_scriptsMDN 上描述了与页面脚本交互的更复杂的方法,但其中一些仅适用于 Firefox。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐