今天的 Greasemonkey 4 只检测顶层的导航事件,因此它有效地将@noframes
应用于每个脚本。
webNavigation.onCommitted 不会“看到”初始框架创建/页面呈现,但如果框架导航到其初始页面以外的某个地方,则侦听器将捕获它。 如果选项包含密钥'allFrames': true
则问题_某种程度上_已解决。 静态 html 页面中的任何框架都会注入脚本,尽管您会遇到来源/网址匹配问题。 此外,如果使用 Javascript 创建框架,则不会注入脚本。
我能想到的最简单的解决方案是将 webNavigation.onCommitted 替换为带有{'urls': ['<all_urls>'], 'types': ['main_frame', 'sub_frame']}
过滤器的 webRequest.onResponseStarted。
我做了一些有限的测试,不需要进一步的改变。 我能够在一个框架内执行一个脚本和一个使用 Javascript 创建的框架。
@arantius 有没有计划从@Sxderp合并修复程序?
这会影响 iframe 是跨域对象的情况,因此如果不对这些特定 iframe 运行 GM,就不可能进行任何类型的修改。
谢谢 !
错过了这个,很快就会研究它。
只是在我测试旧脚本期间的一个观察,不知道,如果它有帮助......
给定一个脚本,它应该在 iframe 上工作,
它看起来好像在执行页面上的更改,
但随后再次刷新到未修改的页面。
当我非常快速地刷新页面时,我只能看到闪烁。
只是在我测试旧脚本期间的一个观察,不知道,如果它有帮助......
这是我的补丁还是发布的版本?
呃,我认为是 4.0 版本...
我是@4.1b3,但对于测试,我很确定,我重新安装了发布版本(直到现在!)
这里与 Eselce 描述的 iframe 相同。 它以某种方式执行,但在 iframe 或页面加载后停止。
如果我注入这个脚本,我只会得到 1 和“self !== top”:
console.log('1');
if (self !== top) {
console.log('self !== top');
setTimeout(function() {
console.log('Timeout');
}, 2000);
} else {
console.log('self === top');
}
“超时”未显示在日志中,所有函数和绑定均未显示。
我使用 4.1b3。
我在 Quantum 上运行 GM 4.0 时遇到了同样的问题。 我写了一个非常简单的虚拟示例,其中包含两个页面: main.html
和framed.html
,以及一个 GM 脚本,该脚本在每个页面上加载并输出它加载的页面的 URL。
大多数情况下,我只会收到关于main.html
通知,但在大约 5% 的情况下,特别是如果我按住 F5,我可能还会收到关于framed.html
的通知。
是否有任何黑客可以可靠地强制 GM 4.0 在 iframe 内执行直到补丁发布?
我刚刚发现用户脚本在<embed src="...">
中可靠地执行,但不在<iframe src="..">
我写了一个小测试脚本:
https://openuserjs.org/scripts/cuzi/iframe_embed_Test_Greasemonkey_4
更多细节:在某些情况下,我的脚本完全在框架中执行(但视图稍后会被页面脚本等覆盖)。
有时,我脚本的同步部分结束了,但是异步部分突然被页面活动中断了......
希望,这有帮助!
有没有人有更多关于这方面的信息?
只是这个线程(问题)的简短摘要:
我不太喜欢这些内部结构,但可能有人是......
作为临时修复,我一直在将 iframe 换成嵌入(示例脚本),这确实可以获取与要触发的框架相对应的脚本(归功于@cvzi发现<embed src="...">
确实有效) .
可能值得注意的是 Violentmonkey 和 Tampermonkey 在嵌入式框架内工作得很好。 由于VM是开源的,也许看看他们是怎么做的?
不幸的是, Violentmonkey 和 Tampermonkey 仍然使用旧的 GM_ 命名方案作为特殊功能,因此脚本尚不可移植。
https://github.com/greasemonkey/gm4-polyfill
Tampermonkey => GM.* 调用,如果未提供
Violentmonkey => GM.* 电话
Greasemonkey -3.17 / FF -56.0 => GM.* 调用
Greasemonkey 4.0+ / FF 57.0+ => 内置 GM.* 调用
// <strong i="11">@grant</strong> GM.getValue
// <strong i="12">@grant</strong> GM.setValue
// <strong i="13">@require</strong> https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
// <strong i="14">@grant</strong> GM_getValue
// <strong i="15">@grant</strong> GM_setValue
@Sxderp的修复是否已合并到主分支中? 如果没有,我该如何安装他的叉子?
@Sxderp的修复是否已合并到主分支中? 如果没有,我该如何安装他的叉子?
git clone -b use-on-response-started-for-execute --single-branch https://github.com/Sxderp/greasemonkey.git
[1]./package.sh
,这将创建一个 XPI 文件。about:config
并将xpinstall.signatures.required
为false
about:addons
,单击齿轮,然后选择从文件安装。[1] 如果您的 git 版本不支持-b
和/或--single-branch
标志(git 的旧版本),那么您可以执行git clone https://github.com/Sxderp/greasemonkey.git
和git checkout use-on-response-started-for-execute
.
你好,
你要解决这个问题吗? 这是一个相当古老的缺陷并影响所有基于 iframes 结构的脚本......
提醒一下,我们明天是 3 月 13 日(请参阅Firefox 59.0
)...
我想参考@Sxderp :
webNavigation.onCommitted 不会“看到”初始框架创建/页面呈现,但如果框架导航到其初始页面以外的某个地方,则侦听器将捕获它。 如果选项包括键 'allFrames': true 问题就解决了。 静态 html 页面中的任何框架都会注入脚本,尽管您会遇到来源/网址匹配问题。 此外,如果使用 Javascript 创建框架,则不会注入脚本。
其实,我有证据,那(我的系统上)监听executeUserscriptOnNavigation
被可靠地称为chrome.webNavigation.onCommitted
,使chrome.tabs.executeScriptInFrame
时调用正确的frameId
. 为什么这不能解决我们所有的框架问题? 我们不应该需要chrome.webRequest.onResponseStarted
来对 iframe 做出反应! (或者你的意思是,它对事件做出反应,但框架不可见?)它绝对被称为......
chrome.tabs.executeScriptInFrame
和frameId
是否存在已知错误? 几年前有问题,但现在已经解决了。 all_frames
未设置,因此frameId
应该是有效的。 将选项matchAboutBlank
为true
似乎很重要(否则executeScript
返回错误<unavailable>
),尽管我并不完全理解about:blank
东西(它在哪里?)...
有任何想法吗?
特征? 这是从一开始就缺少的基本功能......我希望,我误解了这一点。
@Eselce ,这是很久以前的事了,我不确定我是否完全记得我指的是什么,但我会
现在,进入正题。 当我进行初始测试时,我有一个具有相同原始框架和远程框架的静态页面。 在初始页面加载时,我只能让onCommitted
回调触发一次。 它为主文档触发,但没有为文档中的任何静态框架触发 [1]。 因此不会将脚本注入其中。
但是,在初始加载后,如果我使其中一个框架“导航”到某个地方,则会调用onCommitted
回调并将脚本注入新位置的框架中。
为了解决上述问题,尝试使用all_frames
选项键。 虽然使用 key 会导致脚本被注入到页面上的框架中,但明显的缺点是没有正确匹配框架的原点+路径与脚本应该或不应该运行的内容。
顺便说一句,我还提到使用all_frames
键不会将脚本注入到由 Javascript 创建的框架中。
[1] 我不知道这个问题是否仍然存在。 如果我记得这个问题在 FF 52 ESR 中不存在,但在 56 (57?) 中存在(因此是回归)。 也许它已经被修复了。
我几乎在所有方面都同意你的观点。
您是正确的,因为每个框架都是单独匹配的,就好像它是一个完整的标签(有自己的window
和自己的document
,嵌入在框架中)。
嗯,我几乎总是使用相同的菜单/框架结构,所以也许我应该测试一个不同的。
当你说“它被点燃”时,你是指听者的纯粹呼唤吗?
正如我所说,我遇到了一些executeScript
产生错误条件的示例,但仍然调用了侦听器。
all_frames
不能工作,因为错误的location
(不同的window
,不同的document
)。 顺便说一句:菜单只处理每个选项卡的大型机 url - 如果菜单错误,这并不意味着不调用脚本......
当你说“它被点燃”时,你是指听者的纯粹呼唤吗?
在那个特定的消息中,我的意思是“调用了传递给onCommitted.addListener
的函数。”。
你好,
我仔细阅读了这篇文章,但我不明白如何在我的本地脚本“.user.js”上使用您的替代解决方案。 我该如何应用您的解决方案? 对不起,我是新来的。
(自更新 Firefox 后,附加脚本不再识别生成的 popup-iframe,但如果我在新窗口中打开相同的弹出窗口,则会应用该脚本。)
预先感谢您的帮助
您已经准确描述了问题所在:就像@noframes
被激活。 框架内容的 URL 没有正确激活您的脚本。 希望这能尽快解决(在额外的窗口中打开框架很烦人)...
谢谢埃塞尔斯。
并且总是自 Firefox 更新以来,我不得不在标题中声明所有已在目标站点上使用的 scipt '.js'(带有 require)。 (包括jquerry)
它不一样......它会产生错误或冲突。
你也知道这个问题吗?
我不愿意提供我正在测试的特定 URL,因为它们超出了我的控制范围,但我发现一些一致但奇怪的行为可以传递。
我有一个需要与之交互的站点。 它最初在不同的域上加载一个带有rows="100%,0"(一帧来填充屏幕)的框架集/框架。 该帧在中间帧集/帧的域内包含一个帧集/3 个帧。
一些 1+3 子框架的 GM 脚本将“短暂”存在,然后在初始循环后消失——它们在任何异步操作后永远不会回来。 这符合此线程中描述的某些行为。 请注意,哪些“blip”和下面描述的行为因浏览器/GM 版本而异,但不是随机的; 这些模式很奇怪,但对于任何给定的设置都是 100% 可重复的。
此时,“content.htm”的脚本不仅会“闪烁”……它还会在 GM.xmlHttpRequest 完成后保持活动状态——一个异步事件。 此时,“content.htm”在浏览器显示中无处可见,但其脚本将继续运行。
所以在我看来,框架内页面上的 GM 脚本不起作用的原因是因为脚本是在页面卸载而不是页面加载时加载的。 将 @run-at 设置为 document-start 并将脚本放入 unsafeWindow.document 的 DOMContentReady 事件中没有任何改进。 (在 window.document 上设置它永远不会触发事件。)
-瑞安
在 Firefox 52.8 / GM 4.1 中,下一个框架集 / 3 个框架将始终存在。 在 Firefox 60.0 / GM 4.3 中,它们在初始帧加载时不会“闪烁”。
在过渡到 Firefox 57 时,Mozilla 改变了_一些_。 不管它是什么,它改变了(或破坏了)框架的触发方式。 这在其他一些问题中简要讨论过。 因此,脚本不会在初始帧加载 (57+) 上运行。
最终切换到userScript
或contentScript
API 应该可以解决所有这些问题。
所以人们一直在说,但 Violentmonkey 继续在帧中执行。 VM 没有正确地将用户脚本与网页内容分开(CSP 阻止用户脚本,网页可以重写全局对象等),或者我很久以前就已经放弃了 Greasemonkey。 也许这些是相关的问题,但我使用的是用户脚本引擎,所以我不必深入探索 Firefox 的 chrome 以自己找出答案。
嗯,这是一个困难的问题,我的一些脚本不再工作,因为它们不能与 iframe 一起工作。 所以看起来它真的不可能修复它,我们必须让 Mozilla 实现一些 API? 没有什么我们可以自己做的,比如变通办法吗? 我只需要对 iframe 中的页面做一些事情,通常甚至来自同一个域。
目前我自己的解决方法是在顶级框架中运行 Greasemonkey,在子框架中运行 Violentmonkey。 我不能使用 Tampermonkey,因为它有一个不明确的自制许可证,但如果这对您来说不是问题,那么它可能会或可能不会更好地工作。
请记住,VM 将脚本放入页面自己的上下文中。 这就像 GM 的 unsafeWindow 没有安全等价物。 我最难忘的一次是调试一个脚本,其中页面内容在 Array.prototype 上定义了一个“toJSON()”方法,导致 JSON.stringify() 在我的脚本中吐出无效的 JSON。我不得不防御性地陷阱并在我发现它们时修复它们。
VM 的另一个重大问题是它尊重内容页面的内容安全策略,因此任何限制脚本源的指令都将导致 VM 脚本永远不会执行。 您可以在浏览器控制台(但不是 Web 控制台)中看到这一点。 这就是为什么我不能完全运行 VM 而只是摆脱 GM。 我还没有遇到带有 CSP 集的儿童框架,但是当我遇到时,它将完全无法使用。
@RyanHanekamp感谢您的提示! 也许我会在一些脚本中使用 Violentmonkey,然后。
Violent 是否也有类似同步 GM_getValue 的东西? 这是另一个有问题的问题,它破坏了新 Greasemonkey 中的大量脚本。 不过,由于各种原因,我仍然最信任 Greasemonkey,所以我不会离开它。
至于 iframe,我一直在尝试用对象标签替换它们,显然可以使用 Javascript 直接访问它们,如下所示:
myObject= document.createElement('object');
myObject.setAttribute('id', 'myObject');
document.body.appendChild(myObject);
myObject.setAttribute('src', 'https://example.com');
然后,一旦对象被加载:
document.querySelector('#myObject').contentDocument.defaultView.document.querySelectorAll('someElementInsideObjectPage')
至少这在对象与主页位于同一主机的脚本中对我有用。 您还可以从和向( ... contentDocument.defaultView.postMessage('hello, object')
)对象发送消息。
我不是 VM 专家,但我相信它至少实现了大部分原始 GM_* API。 不过,从长远来看,我认为让脚本适应异步比回溯到同步平台更好。 根据我的理解,Greasemonkey 这样做是为了在新的 Quantum 框架内提升性能,该框架不允许在后台和内容脚本之间进行同步调用。
至于对象解决方案,它不会解决我的特定问题,但我很高兴其他人发现它很有用。 除了封送 CSS / 属性 / 等并使其与框架和 iframe 一起使用之外,我还必须过滤掉捕获过程中可能不安全的对象。 有很多方法可以解决所有这些问题,但在 GM 最终按照它所说的去做之前,VM 是更容易的过渡。
此外,如果您有同源框架/iframe,您也可以直接访问它们的内容。 更难的部分是跨域,这就是为什么我需要框架内的用户脚本。 它设置了一个 window.postMessage() 通道来与父窗口对话。
@RyanHanekamp很
我明白为什么 Violent 对你来说是更好的选择。 让我们希望 Anthony 或 Sxderp 或其他人最终找到如何实现这一点。 我真的希望我能做出贡献,但我完全是个外行。
哦,你可以直接访问同源iframe的内容(没有postMessage等)? 我记得我花了很多时间试图找到一种方法,但似乎不可能。 这就是我也切换到 postMessage 的原因。
Frames 和 iframes 有一个 contentWindow 属性,它等同于 window 属性。 两者都有一个文档属性来访问 DOM。
使用 iframe(在同一个源上)最难的部分是检测其内容何时加载,因为在此之前您无法进行深蹲。 onload 看起来不起作用。 Firefox 提供了一个 DOMFrameContentLoaded 事件,该事件会为加载的每个框架(包括孙/曾孙等框架)触发,您可以使用 event.target 属性将其与原始框架/ iframe 元素匹配。 如果您控制 frame/iframe 的内容,您还可以使用 postMessage 或调用 window.parent 对象上的全局方法让它与父对象对话。
说起来......这是这个问题的潜在解决方法。 如果有或可能有一种方法来编写 GM 脚本以将用户脚本手动注入到跨域窗口引用中,那么用户脚本创建者将需要进行更多的编码,但它可以完成工作。 该模式将侦听 DOMFrameContentLoaded,检查 event.target 是否为第一代,如果是,则手动注入脚本。 (假设第一代框架的脚本可以监听第二代框架的DOMContentLoaded,从而得到一个完整的链。)没有办法得到@run-at dom-start 行为,也可能有时间问题,但是对于大多数用例,我们可能会解决这些问题。
我个人已经放弃了这个问题的解决,转而直接对扩展进行编码。 在所有帧中都可以正常工作!
在这一点上,Greasemonkey 和 Violentmonkey 之间的区别似乎是 Violentmonkey 是从 all_frames 设置为 true 的内容脚本触发的,而 Greasemonkey 没有安装时内容脚本,并且完全依赖后台脚本的可疑能力来嗅探选项卡的框架已导航。 (并且 Violentmonkey 在 CSP 页面上失败,因为它临时注入了 SCRIPT 标签,而不是使用更安全的 tabs.executeScript()。)
放入带有 all_frames 的静态内容脚本,run_at start,匹配所有内容以通知后台进程 start / document.DOMContentLoaded / document.Idle 为每个 run_at 触发用户脚本,您就可以开始了。 使这个问题消失的非平凡但可管理的工作量。 我会自己修复它,但我对通过你的开发依赖项不感兴趣并且只能生成输出代码。
@RyanHanekamp
而是转而直接对扩展进行编码。 在所有帧中都可以正常工作!
你愿意分享你的扩展代码吗?
我的扩展不是通用的。 关键是在清单中使用带有 all_urls 的静态 content_script,all_frames 将在加载或导航任何框架时运行脚本,甚至可以很好地评估 / 函数构造函数代码,而不管页面的内容安全策略如何。
我还没有对以编程方式构建的框架/窗口进行测试,但我认为无论 run_at 设置如何,它们都会在初始创建时运行,因为这些框架最初创建为空白然后填充——引擎可能只会看到初始创建。 我也没有测试 data: urls,这可能需要显式匹配——我不确定 all_urls 是否涵盖它们,或者只是 http/https。
它也可能不必是静态 content_script 引用,但从文档中看,如果在导航页面时自动加载动态调用的内容脚本,则似乎不确定。 我的印象是它们仅根据提供的匹配模式注入当前匹配的选项卡/框架中,并且导航不会触发重新注入。 但是我认为为此目的使用静态 content_script 没有任何缺点。
Greasemonkey 显然不能将用户脚本作为静态资源包含在清单中,并且内容脚本似乎不能直接访问 tabs.executeScript(虽然我不是这方面的专家,因为我才刚接触几天),但是静态内容脚本可以做的是向后台进程发送消息,让它知道 frameId 已被导航,以及导航到什么 URL。 这比我如何看待该线程上提到的在 webRequest 或 webNavigation 中挂钩正确事件的尝试更可靠。 来自静态 content_script 的信号成为我们正在寻找的事件,以触发 Greasemonkey 的用户脚本加载器/注入器。
对于严格必须 run_at document_start 的用户脚本,可能会有延迟。 对后台脚本的调用是异步的,文档将在调用用户脚本时进行。 这很可能是 Violentmonkey 使用临时脚本标签而不是 tabs.executeScript 的原因,因为脚本标签注入可以直接从 content_script 同步完成。 我会发现被迫解决文档状态的不确定性让 run_at document_start 很麻烦,但比根本不运行的脚本更可取。
我可以在带有以下 CSP 集的 Firefox 60.0.1 和 52.8.1ESR 上直接从 content_script 中执行我自己的插件,包括一个 Function() 构造函数:
帧源数据:; object-src '无'; 脚本源代码“无”; style-src 'unsafe-inline' 数据:; 连接-src '无'; media-src '无';
几个月前首次提交此文件时,我们的做法有所不同。 今天我们使用webNavigation.onCommitted来检测导航,并且在我现在正在运行的测试中,由于某种原因我没有看到它在 (ni) 帧上触发)。
你好,请问现在解决了吗?
我无法使用 Greasemonkey 在 iframe 上启动我为 Tampermonkey 创建的脚本。
执行代码在我们这边没有改变。 因此,除非 Mozilla 最终改变了某些内容,否则这仍然是失败的。 但是#2663 应该可以解决它。
可能值得注意的是 Violentmonkey 和 Tampermonkey 在嵌入式框架内工作得很好。
Tampermonkey 在 Chrome 中的 iframe 有问题,至少对我来说是这样。
可能值得注意的是 Violentmonkey 和 Tampermonkey 在嵌入式框架内工作得很好。
Tampermonkey 在 Chrome 中的 iframe 有问题,至少对我来说是这样。
但它在 Firefox Violentmonkey 中对我有用。 所以我想知道它如何在那里工作?
我刚刚注意到使用旧同步 GM_GetValue 的脚本在 Violentmonkey 中仍然可以正常工作。 这怎么可能? 我以为 Firefox 强制异步 GM.GetValue? 我现在很困惑:大概 Violentmonkey 必须牺牲其他东西才能仍然支持同步和其他东西?
@Cerberus-tm Firefox 中的更改意味着只能从扩展存储或后台上下文异步请求数据(用户脚本本身被异步发送到内容脚本)。 但是,如果每个 GM4 内容脚本都预取并缓存在内容脚本中,那么数据可以同步提供给用户脚本,这些数据是为正在加载到该页面的每个用户脚本存储的。
这样的缓存将允许内容脚本同步响应用户脚本的数据请求。 实现缓存会遇到在各种内容脚本之间保持一致性的问题,但这可以通过让内容脚本侦听 GM4 扩展存储的所有更改来解决。 此外,如果用户脚本存储大量数据,那么在运行用户脚本的每个内容脚本中缓存它也会显着增加每个内容脚本所需的内存。
TM 和 VM 选择做与上述类似的事情,以便在为 Chrome 实现这些用户脚本管理器时不破坏与原始 Greasemonkey API 的兼容性,Chrome 具有相同的限制。 与扩展存储等的异步通信。鉴于他们已经在 Chrome 上这样做了,因此在为 Firefox 实现时没有理由改变它们。
因此,FF57 切换到 WebExtensions 确实强制重写了 GM,但它并没有强制采用GM.getValue
、 GM.setValue
等的异步 API。WebExtensions 确实使用了基于异步的 API 比同步更容易实现,但它并没有使其成为必需。
就个人而言,我觉得这个选择和其他破坏兼容性的选择是不幸的。 与在 GM3 中运行良好的脚本缺乏向后兼容性和/或与 TM 的兼容性导致许多人选择不使用 GM4。 我的经验是,我使用的超过 30 个用户脚本都在 GM3 中运行良好,但不适用于 GM4(或者至少在被重写以与 GM4 兼容之前不起作用)。 我每天仍然使用 28 个用户脚本,它们在 GM3 下运行良好,但不适用于 GM4。
我在 Stack Overflow 上发布了一个解决这个问题的方法,作为如何将 Greasemonkey/Tampermonkey/userscript 应用到 iframe的答案。 基本上,我正在等待框架加载,然后我在window.frames
数组上进行操作。 我在每个帧的主体中使用自定义标记来表示我已经看过该帧。
也许 Greasemonkey 可以以类似的方式实施解决方案?
如果我们也有像waitForKeyElements()这样的GM.waitFor(css_selector, action_function)
那就太棒了,但这是一个旁白。
最有用的评论
你好,
你要解决这个问题吗? 这是一个相当古老的缺陷并影响所有基于 iframes 结构的脚本......