来自firefox Addon SDK中内容脚本的回调

时间:2014-05-20 05:29:24

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

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

main.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()的选项卡。

1 个答案:

答案 0 :(得分:2)

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

传递<img>.src应该没问题,因为这是一个字符串,因此JSON可序列化。 您的代码因您尝试传递的回调函数而中断,因为function 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元素,再次将其从地图中删除(我们不会#39) ; t想泄漏它的东西)并做任何需要的处理;在这个例子中只记录东西。