【问题标题】:Callbacks from content script in firefox Addon SDKFirefox Addon SDK 中内容脚本的回调
【发布时间】:2014-05-20 15:36:13
【问题描述】:

我正在使用 Firefox addon-sdk 开发一个小型扩展,它必须更改页面中 DOM 元素的内容。我正在使用PageMod 添加内容脚本并注册一些事件,其中一些我想将回调函数传递给,如下所示:

ma​​in.js

pageMod.PageMod({
    include: "*",
    contentScriptWhen: 'ready',
    contentScriptFile: [self.data.url("my_content_script.js")],
    onAttach: function(worker) {
        worker.port.on("processElement", function(elementSrc, callback) {
            doSomeProcessingViaJsctypes();
            callback("http://someUrl/image.png");
        });
    }
});

my_content_script.js

var elements = document.getElementsByTagName("img");
var elementsLength = elements.length;
for (var i = 0; i < elementsLength; i++) 
{

    (function(obj) {
        obj.setAttribute("data-processed", "true");
        self.port.emit("processElement", obj.src, function(newSrc) {
            console.log("replaced " + obj.src);
            obj.src = newSrc;
        });
    })(elements[i]);
}

错误

TypeError: callback is not a function
Stack trace:
.onAttach/<@resource://gre/modules/XPIProvider.jsm -> file:///c:/users/sebast~1/appdata/local/temp/tmpjprtpo.mozrunner/extensions/jid1-gWyqTW27PXeXmA@jetpack/bootstrap.js -> resource://gre/modules/commonjs/toolkit/loader.js -> resource://jid1-gwyqtw27pxexma-at-jetpack/testextension/lib/main.js:53

我似乎无法在网上找到任何关于此事的信息。我需要这种方法,因为处理需要一些时间并且依赖于 .dll 文件,所以我不能从内容脚本中调用它。

如果我要处理元素,然后调用worker.port.emit(),我将不得不再次遍历整个树以识别元素并更改它的src 属性。这将花费很长时间,并且会为文档中的每个 img 添加额外的循环。

我考虑过生成一个唯一的类名并将其附加到元素的类中,然后调用getElementsByClassName()。我没有对此进行测试,但在我看来,这将花费与我上面描述的过程相同的时间。

任何建议将不胜感激。

编辑:我在另一个问题上找到了this answerWladimir Palant 建议使用window-utils 来获取activeBrowserWindow,然后彻底迭代它是content

他也提到了

这些低级 API 不能保证稳定,window-utils 模块甚至没有完整的文档记录

从那以后有什么变化吗?我想知道您是否可以使用选项卡获得相同的content 属性,以及您是否可以识别工作人员发送self.port.emit() 的选项卡。

【问题讨论】:

    标签: javascript firefox firefox-addon firefox-addon-sdk


    【解决方案1】:

    在内容脚本和模块 (main.js) 之间使用消息传递时,您只能传递 JSON 可序列化的数据。

    传递&lt;img&gt;.src 应该没问题,因为这是一个字符串,因此可以进行 JSON 序列化。 您的代码由于您尝试传递的回调函数而中断,因为 functionnot JSON 可序列化的(与整个 DOM 节点不可 JSON 序列化相同)。 此外,.emit.on 仅使用第一个参数作为消息负载。

    在完成处理后,您实际上必须将另一条消息emit 返回到内容脚本,而不是回调。而且由于您无法传递 DOM 元素,因此您需要跟踪哪个 DOM 元素属于哪个消息。

    好的,例如,我会这样做。 先main.js

    const self = require("sdk/self");
    const {PageMod} = require("sdk/page-mod");
    
    function processImage(src) {
      return src + " dummy";
    }
    
    PageMod({
      include: "*",
      contentScriptWhen: 'ready',
      contentScriptFile: [self.data.url("content.js")],
      onAttach: function(worker) {
        worker.port.on("processImage", function(data) {
          worker.port.emit("processedImage", {
            job: data.job,
            newSrc: processImage(data.src)
          });
        });
      }
    });
    

    在我的设计中,每条 processImage 消息都有一个与之关联的 job(请参阅内容脚本),main.js 认为它不透明,只是逐字逐句回复。

    现在,data/content.js,又名。我的内容脚本:

    var jobs = new Map();
    var jobCounter = 0;
    
    self.port.on("processedImage", function(data) {
      var img = jobs.get(data.job);
      jobs.delete(data.job);
      var newSrc = data.newSrc;
      console.log("supposed replace", img.src, "with", newSrc);
    });
    
    for (var i of document.querySelectorAll("img")) {
      var job = jobCounter++; // new job number
      jobs.set(job, i);
      self.port.emit("processImage", {
        job: job,
        src: i.src
      });
    }
    

    所以本质上,对于每个图像,我们将创建一个作业编号(可以是 uuid 或其他任何东西,但是对于我们的用例来说,增加一个计数器就足够了),并将与该作业编号相关联的 DOM 图像放入地图跟踪它。 之后,只需将消息发布到main.js

    processedImage 处理程序,将接收回作业编号和新源,使用作业编号和 jobs 映射取回 DOM 元素,再次将其从映射中删除(我们不想泄漏它的东西) 并进行所需的任何处理;在这个例子中只是记录一些东西。

    【讨论】:

    • 这是一个非常好的和优雅的解决方案。非常感谢。我已经对其进行了测试,即使在 jsctypes 后面进行处理,它的运行速度也非常快。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多