Firefox WebExtension:如何在禁用/卸载之前运行代码?

2023-11-29

我最近将我的 GreaseMonkey 脚本转换为 WebExtension,只是为了获得该过程的第一印象。现在我已经达到了这样一个地步,当所述扩展被禁用/卸载时,最好进行一些清理或简单地撤消我的所有更改。

从我在 Mozilla 页面上看到的内容来看,运行时.onSuspend应该可以解决问题。不幸的是,它看起来还没有实现(我在常规的 Firefox 发布频道上)。

换句话说,我想要做的是由于用户删除/禁用我的扩展而运行代码,以便我可以清理侦听器等,并且通常将选项卡恢复到其状态,即。即,撤消扩展程序所做的所有更改。


另一个答案是不正确的。第一部分(关于onSuspend事件)实际上是不正确的。关于的部分setUninstallURL是相关的,但不回答问题,因为它不允许您将选项卡恢复到原始状态(正如您在问题中询问的那样)。

在这个答案中,我将首先澄清关于runtime.onSuspend,然后解释如何在禁用扩展时运行内容脚本的代码。

About runtime.onSuspend

The chrome.runtime.onSuspend and chrome.runtime.onSuspendCanceled事件与禁用/卸载的扩展无关。事件定义为活动页面,它们基本上是在一段时间不活动后挂起(卸载)的后台页面。当活动页面因暂停而即将卸载时,runtime.onSuspend叫做。如果在此事件期间调用扩展 API(例如发送扩展消息),暂停将被取消并触发onSuspendCanceled event.

当扩展因浏览器关闭或卸载而卸载时,扩展的生命周期无法延长。因此,您不能依赖这些事件来运行异步任务(例如从后台页面清理选项卡)。

此外,这些事件在内容脚本中不可用(仅在后台页面等扩展页面),因此不能使用这些事件来同步清理内容脚本逻辑。

从上面应该可以明显看出runtime.onSuspend is not与禁用后的清理目标远程相关。 Chrome 中不行,更不用说 Firefox 了(Firefox 不支持事件页面,这些事件将毫无意义)。

在扩展禁用/卸载时在选项卡/内容脚本中运行代码

Chrome 扩展中的常见模式是使用port.onDisconnect事件来检测后台页面已卸载,并使用该事件来推断扩展程序可能已卸载(与该方法的选项1以达到更高的准确度)。 Chrome 的内容脚本在扩展被禁用后仍保留,因此可用于运行异步清理代码。
这在 Firefox 中是不可能的,因为在禁用 Firefox 扩展时,内容脚本的执行上下文会在执行之前被破坏。port.onDisconnect事件有机会触发(至少,直到bugzil.la/1223425是固定的)。

尽管有这些限制,当禁用加载项时,仍然可以运行内容脚本的清理逻辑。该方法基于以下事实:在 Firefox 中,样式表插入tabs.insertCSS当加载项被禁用时将被删除。
我将讨论利用这一特性的两种方法。第一种方法允许执行任意代码。第二种方法不提供任意代码的执行,但如果您只想隐藏一些扩展插入的 DOM 元素,则它更简单且足够。

方法一:禁用扩展时在页面中运行代码

观察样式变化的方法之一是声明CSS 过渡 and 使用转换事件来检测 CSS 属性更改。 为了使它有用,您需要构造一个样式表,使其只影响您的 HTML 元素。因此,您需要生成一个唯一的选择器(类名、ID...)并将其用于您的 HTML 元素和样式表。

这是您必须放入后台脚本中的代码:

chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
    if (message !== 'getStyleCanary') return;

    // Generate a random class name, insert a style sheet and send
    // the class back to the caller if successful.
    var CANARY_CLASS = '_' + crypto.getRandomValues(new Uint32Array(2)).join('');
    var code = '.' + CANARY_CLASS + ' { opacity: 0 !important; }';
    chrome.tabs.insertCSS(sender.tab.id, {
        code,
        frameId: sender.frameId,
        runAt: 'document_start',
    }, function() {
        if (chrome.runtime.lastError) {
            // Failed to inject. Frame unloaded?
            sendResponse();
        } else {
            sendResponse(CANARY_CLASS);
        }
    });
    return true; // We will asynchronously call sendResponse.
});

在内容脚本中:

chrome.runtime.sendMessage('getStyleCanary', function(CANARY_CLASS) {
    if (!CANARY_CLASS) {
        // Background was unable to insert a style sheet.
        // NOTE: Consider retry sending the message in case
        // the background page was not ready yet.
        return;
    }

    var s = document.createElement('script');
    s.src = chrome.runtime.getURL('canaryscript.js');
    s.onload = s.remove;
    s.dataset.canaryClass = CANARY_CLASS;

    // This function will become available to the page and be used
    // by canaryscript.js. NOTE: exportFunction is Firefox-only.
    exportFunction(function() {}, s, {defineAs: 'checkCanary'}); 

    (document.body || document.documentElement).appendChild(s);
});

我在上面使用了脚本标签,因为这是在页面中运行脚本而不被页面内容安全策略阻止的唯一方法。确保您添加canaryscript.js to web_accessible_resources在清单.json中,否则脚本将无法加载。

如果运行清理代码并不重要(例如,因为您还使用我稍后解释的方法 2),那么您最好使用内联脚本而不是外部脚本(即使用s.textContent = '<content of canaryscript.js>'代替s.src = ...)。这是因为使用.src与扩展资源介绍了Firefox 的指纹识别漏洞(错误 1372288).

这是内容canaryscript.js:

(function() {
    // Thes two properties are set in the content script.
    var checkCanary = document.currentScript.checkCanary;
    var CANARY_CLASS = document.currentScript.dataset.canaryClass;

    var canary = document.createElement('span');
    canary.className = CANARY_CLASS;
    // The inserted style sheet has opacity:0. Upon removal a transition occurs.
    canary.style.opacity = '1';
    canary.style.transitionProperty = 'opacity';
    // Wait a short while to make sure that the content script destruction
    // finishes before the style sheet is removed.
    canary.style.transitionDelay = '100ms';
    canary.style.transitionDuration = '1ms';
    canary.addEventListener('transitionstart', function() {
       // To avoid inadvertently running clean-up logic when the event
       // is triggered by other means, check whether the content script
       // was really destroyed.
       try {
            // checkCanary will throw if the content script was destroyed.
            checkCanary();
            // If we got here, the content script is still valid.
            return;
        } catch (e) {
        }
        canary.remove();

        // TODO: Put the rest of your clean up code here.
    });
    (document.body || document.documentElement).appendChild(canary);
})();

注意:CSS 转换事件仅在选项卡处于活动状态时才会触发。如果选项卡处于非活动状态,则在显示选项卡之前不会触发转换事件。

Note: exportFunction是仅适用于 Firefox 的扩展方法,用于在不同的执行上下文中定义函数(在上面的示例中,该函数是在页面上下文中定义的,可用于该页面中运行的脚本)。

所有其他 API 也可在其他浏览器(Chrome/Opera/Edge)中使用,但代码不能用于检测禁用的扩展,因为样式表来自tabs.insertCSS卸载后不会被删除(我只使用 Chrome 进行测试;它可能在 Edge 中工作)。

方法二:卸载后视觉恢复

方法 1 允许您运行任意代码,例如删除在页面中插入的所有元素。作为从 DOM 中删除元素的替代方法,您还可以选择通过 CSS 隐藏元素。
下面我展示了如何修改方法 1 以隐藏元素而不运行其他代码(例如canaryscript.js).

当您的内容脚本创建要插入到 DOM 中的元素时,您可以使用内联样式隐藏它:

var someUI = document.createElement('div');
someUI.style.display = 'none'; // <-- Hidden
// CANARY_CLASS is the random class (prefix) from the background page.
someUI.classList.add(CANARY_CLASS + 'block');
// ... other custom logic, and add to document.

在您添加的样式表中tabs.insertCSS,然后您定义所需的display值,与!important标记以便覆盖内联样式:

// Put this snippet after "var code = '.' + CANARY_CLASS, above.
code += '.' + CANARY_CLASS + 'block {display: block !important;}';

上面的例子是故意通用的。如果您有多个具有不同 CSS 的 UI 元素display值(例如block, inline,...),然后您可以添加多行这样的行来重新使用我提供的框架。

为了显示方法 2 相对于方法 1 的简单性:您可以使用相同的后台脚本(经过上述修改),并在内容脚本中使用以下内容:

// Example: Some UI in the content script that you want to clean up.
var someUI = document.createElement('div');
someUI.textContent = 'Example: This is a test';
document.body.appendChild(someUI);

// Clean-up is optional and a best-effort attempt.
chrome.runtime.sendMessage('getStyleCanary', function(CANARY_CLASS) {
    if (!CANARY_CLASS) {
        // Background was unable to insert a style sheet.
        // Do not add clean-up classes.
        return;
    }
    someUI.classList.add(CANARY_CLASS + 'block');
    someUI.style.display = 'none';
});

如果您的扩展有多个元素,请考虑缓存以下值CANARY_CLASS放在局部变量中,以便每个执行上下文仅插入一个新样式表。

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

Firefox WebExtension:如何在禁用/卸载之前运行代码? 的相关文章

  • 解决错误 413 请求实体太大

    我正在从事的项目允许我们的员工将大文件上传到我们的共享主机并获取下载链接 问题是我们的托管拒绝更改共享托管的 LimitRequestBody 还有其他解决方案可以解决 LimitRequestBody 或任何其他方法来完成这项工作吗 有两
  • 继续使用 sketch.js 编辑草图图像 [关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 我正在使用 sketch js 中的示例 http intridea github io sketch js http intridea g
  • 是否有匹配单个字素簇的正则表达式?

    字素是用户感知的文本字符 在 unicode 中可能由多个代码点组成 From Unicode 标准附录 29 http unicode org reports tr29 Grapheme Cluster Boundaries 重要的是要认
  • 为什么电子邮件正文给出不同的输出?

    我正在尝试触发来自 Google 应用程序脚本的电子邮件 const body HtmlService createHtmlOutput A b new task b have been added to the Task Manager
  • 如何通过 JS 中的 WebPack 包提供全局 TypeScript 类

    我目前正在研究 TypeScript 我想用 TS 替换 JS 但是我有很多 JS 文件 所以我只想在 TS 中创建新类 并想在我的旧 JS 文件 atm 中使用这些类 后来我想把所有的JS都换成TS 我对 webpack 和捆绑的 js
  • 差异:查看页面源代码与在 Firebug 中查看

    当我查看页面的页面源时 例如 http my sa ucsb edu public curriculum coursesearch aspx http my sa ucsb edu public curriculum coursesearc
  • Razorpay 支付集成 -> 我如何检测关闭按钮 X 附近的 razorpay 模型

    我在 CI 框架中使用 Razorpay 当用户在没有付款的情况下关闭时 创建 razor 支付模型 然后对于取消订单 我希望通过状态更改为已取消来触发查询 那么我怎样才能检测到这一点 我已经在使用 by click jQuery 点击关闭
  • 替换字符以制作国际字母(变音符号)

    我正在尝试模仿国际键盘的工作方式 如果您使用其中之一死钥匙 http en wikipedia org wiki Dead key后面跟着一个字母 它将它们组合成相应的字符 例如 输入 a会导致 and o结果是 etc 我似乎无法让我的正
  • 使用 Javascript 在前端创建基本 URL(开发、API 和生产)

    无论开发和部署如何 如何制作适用于 http https localhost 端口和实际域的基本 url 我想创建一个可以在所有场景或条件下工作的基本 url 无论 http https 协议 端口 本地主机和实际域如何 无论是在开发中还是
  • 在全局范围内查找 JavaScript 函数

    有没有办法在当前活动的对象模型中搜索 JavaScript 属性 例如命名函数 Firebug 在 DOM 选项卡上显示的内容 我在 Chrome 开发人员工具中找不到直接等效项 加载页面 使用主要浏览器的通用开发人员工具 一个例子是 我搜
  • 在 MongoDB 中对 Null 值进行最后排序

    我使用以下查询根据名为 sortIndex 的字段按升序填充 MongoDB 中的项目 有时 数据库中的项目没有 sortIndex 字段 通过以下查询 具有 null sortIndex 的项目显示在顶部 我想知道如何让它们显示在底部 我
  • 如何以编程方式确定 HTML 对象可以侦听哪些事件?

    我一直在查看developer mozilla org 和Apple 开发文档上的文档 但我找不到解释是否可以通过编程方式确定特定HTML 标记是否支持给定事件监听器的文档 就像我知道的那样
  • 如何调试使用 Testaulous (Karma) 运行的 Jasmine 规范?

    我有一个具有 Jasmine 规格的小项目 我使用 Testaulous 作为我的测试运行程序 我不明白如何调试应用程序代码或规范代码 当我尝试在 Chrome 开发工具中设置断点时 下次规范运行时不会命中它 因为它每次都会使用新的查询字符
  • 量角器检查元素是否不存在

    我在基于角度的网站中有一个设置可以打开和关闭下拉菜单 如果关闭 则不会显示在主页上 对于量角器 我需要检查开关关闭时该元素是否不存在 但是 我不应该陷入 未找到元素 错误 因为它是一组许多测试中的一个 我该怎么做 我曾尝试这样做 expec
  • 如果是数字,Chrome 会重新排序对象键,这是正常/预期的吗

    我注意到某些评估电子商务网站的某些鞋码并将其输出到屏幕上的代码会打乱 Chrome 中的顺序 给出的 JSON 可以是 7 9149 9139 10455 17208 7 5 9140 9150 10456 17209 8 2684 914
  • 如何在 Yii 框架中使用 jQuery?

    如何在 yii 中使用 jquery javascript 如何在 yii 中使用我的脚本 为什么这与以其他方式使用 jQuery 有什么不同 如何在yii中使用jquery 如上所述 您可以注册新的脚本块 也可以注册新的外部脚本文件 您还
  • 如何从客户端 JavaScript 调用特定的 Node.js 方法

    在我的应用程序中 我在 node js 文件中创建了许多方法 我如何从客户端 JavaScript 调用特定方法 下面是我的node js 文件 exports method1 function exports method2 functi
  • Img src 路径以及要传递的标头参数

    我在 jsp 页面中有一个 img 标记 其中 src 路径需要传递标头参数才能获取图像 我们怎样才能实现它呢 您现在可以使用fetch https developer mozilla org en US docs Web API Fetc
  • 使用 JavaScript 和 HTML 打印表情符号

    为什么这有效 p x1f604 p 而这并没有 document getElementById emoji innerHTML String fromCharCode parseInt 1f604 16 JS 术语中的 char 实际上是一
  • React 不适用于 Android 4.4.2 Web 视图浏览器

    React 在我的 Android 手机上不起作用 我不认为这是我的代码错误 因为即使我写的唯一内容是 p hello world p 它仍然没有显示 在 chrome 远程吊顶上查看后 这显示了 Map is not defined 以前

随机推荐

  • 将内联 SVG 转换为 Base64 字符串

    我想将内联 SVG 图像发送到 PHP 脚本 以使用 Imagick 将其转换为 PNG 为此 我必须知道如何在内联 SVG 上获取 Base64 字符串 对于画布对象 它是一个简单的 toDataURL 但这不适用于内联 SVG 因为它不
  • 带有 Facelets (jsf) 和 xhtml 的 Eclipse 自动完成(内容辅助)

    如何使用 Facelets 在 xhtml 页面中为 JSF 1 2 标记激活 Eclipse 3 5 WTP 内容辅助 它适用于 jsp 文件 但不适用于 xhtml 我用谷歌搜索了很多 在很多地方 包括 MyFaces wiki 都说
  • Python - 使用 pyqtgraph 快速绘图(16ms)?

    我需要使用 pyqtgraph 绘制连续输入 因此我使用循环缓冲区来保存数据 我使用 deque 和 maxlen 来完成这项工作 Python 2 7 numpy 1 9 2 pyqtgraph 0 9 10 from collectio
  • 在 上添加边框

    在另一篇文章中 我读到 如果我需要为除标题行之外的每一行添加边框 我应该使用 THEAD TBODY 所以我已将其添加到页面中 但我找不到如何将其应用到 TBODY 我是新手 所以请耐心等待 我可以在整个表格周围放置边框 但需要排除标题行
  • 实体框架代码优先软删除延迟加载

    所以我首先使用实体 框架代码 所以没有 edmx 我有一个带有 bool IsEnabled 的基实体类来执行软删除 我正在使用存储库模式 因此可以使用 IsEnabled 过滤掉针对存储库的所有查询 但是 每当我使用存储库获取 IsEna
  • 使用 str.split 函数拆分数据框中的列

    我试图将带有逗号分隔值的列拆分为 2 列 但 str split 函数返回带有 0 和 1 的列 而不是拆分字符串值 我有一个数据框 其中有一列 全名 其中有一个全名 并用逗号分隔姓氏和名字 我使用了 str split 函数 该函数在执行
  • 为什么 C++ Map 的 [] 运算符调用映射值的默认构造函数? [复制]

    这个问题在这里已经有答案了 我用g 编译了以下代码 执行该行时将调用构造函数A m 1 为什么会发生这种情况 我认为没有必要在这里调用构造函数 struct A int mem A int arg A int main unordered
  • 将所有 标签替换为 img 替代文本

    我知道如何在 php 中执行此操作 但我需要在 javascript jquery 中完成此操作 我正在尝试类似以下的事情 NewBox html OldBox html Replace
  • 对象使用原型函数名称而不是其属性

    我创建了两个函数A和B 将A作为B的原型 jsfiddle function A function B this name Class B B prototype A var b new B alert b name expected Cl
  • 使用 VBA 查找 MS Office 修订版和内部版本

    The 主要版本和次要版本可以使用 Office 应用程序找到Application Version 返回示例 15 0 Office 2013 12 0 Office 2007 我需要修订版和构建版本Office 应用程序的示例 微软 O
  • 如何使用 Autofac 和 ASP.NET Core 在控制器上启用属性注入?

    似乎确实没有一种简单的方法来确保控制器具有属性注入 解决这个问题的方法是单独注册所有控制器 这似乎有点违背了目的 这 FromServices 属性是removed他们特别提到应该由各个 IoC 容器来确保这种情况发生 我是否遗漏了一些明显
  • 使用 Spring Data JPA 时我的存储库无法自动初始化

    在探索了几个教程后 我尝试将 spring data jpa 添加到我的 spring mvc web 项目中 但我发现我的存储库无法自动初始化 我的服务类中出现 NullPointerException 请看我下面的示例代码 我的存储库
  • Lua 编程 - os.execute() 在 Windows 中不起作用

    我正在 pure Lua 中创建一个函数来扫描目录中的文件并将它们放在另一个文件中 我尝试的命令是 os execute dir B C Users Fernando workspace Organizator2 s1 gt C Users
  • 如何在eclipselink中访问多个租户?

    eclipselink 或 Hibernate 中的租户是一个很好的概念 可以将数据域彼此分开 我正在使用 eclipselink 和单表策略 有时需要访问多个租户的数据 例如出于管理目的 有没有好的方法可以实现这一目标 我不想跑遍所有租户
  • 将图表坐标转换为像素

    我应该在极坐标图中画一个圆圈 上面有一些文字 我开始使用 PostPaint 获得了图表图形 因此我能够在上面绘制和编写自定义内容 我的主要问题是位置 例如 我想在 x 和 y 轴交叉的地方绘制 sg 但我没有找到任何有效的方法将图形坐标
  • firebase subscribeToTopic OnCompleteListener 没有被调用

    我正在开发一个 Android 项目 其中用户将订阅 Firebase 主题 我写了如下代码 FirebaseMessaging getInstance subscribeToTopic update addOnCompleteListen
  • 使用 OpenSSL 的客户端/服务器程序的文档

    我有一个用 C 编写的 TCP 客户端 服务器程序 我想使用 OpenSSL 保护交换的数据 这对我来说很新 我在网上找不到示例 您能指出一些关于此事的 googd 文档吗 谢谢你 检查以下链接 他们应该有帮助 http www linux
  • 有人做过 Osherove 的 TDD Kata“字符串计算器”的 Objective-C Xode 版本吗?

    总是尝试更好地编码 我对针对 Objective C 和 Xcode 进行 TDD 很感兴趣 你知道有什么帖子可以实现类似的功能吗Roy Osherove 的 字符串计算器 Kata Update 尝试找出如何在 iOS 上加速 TDD 我
  • 如何在 Tkinter 文本搜索方法中使用正则表达式?

    我想在方括号内找到一个突出显示的区域 这些方括号可以包含任何文本 A hello world B this is a tree A tkinter documentation is bad I want to highlight the w
  • Firefox WebExtension:如何在禁用/卸载之前运行代码?

    我最近将我的 GreaseMonkey 脚本转换为 WebExtension 只是为了获得该过程的第一印象 现在我已经达到了这样一个地步 当所述扩展被禁用 卸载时 最好进行一些清理或简单地撤消我的所有更改 从我在 Mozilla 页面上看到