Javascript Firefox扩展(SDK)中的内容脚本添加事件侦听器

Javascript Firefox扩展(SDK)中的内容脚本添加事件侦听器,javascript,firefox,firefox-addon,firefox-addon-sdk,Javascript,Firefox,Firefox Addon,Firefox Addon Sdk,我的Firefox扩展提供了一个JavaScript函数,网站可以使用该函数访问插件功能。网站调用此函数并提供两个回调 网站代码: function onButtonClick() { var callbackSuccess = function() { alert("Yeah!"); }; var callbackError = function() { alert("Oh no!"); }; if (window.magicAddon) { // Check if my addo

我的Firefox扩展提供了一个JavaScript函数,网站可以使用该函数访问插件功能。网站调用此函数并提供两个回调

网站代码:

function onButtonClick() {
  var callbackSuccess = function() { alert("Yeah!"); };
  var callbackError = function() { alert("Oh no!"); };
  if (window.magicAddon) { // Check if my addon is installed
    magicAddon.doStuff(callbackSuccess, callbackError);
  }
}
unsafeWindow.magicAddon = {
  doStuff: function(callbackSuccess, callbackError) {
    // Bind the two callbacks to events. The addon will fire one of them
    self.port.on("doStuffSuccess", callbackSuccess);
    self.port.on("doStuffError", callbackError);

    // Fire the event that lets the addon do stuff
    self.port.emit("doStuff");
  }
};
内容脚本:

function onButtonClick() {
  var callbackSuccess = function() { alert("Yeah!"); };
  var callbackError = function() { alert("Oh no!"); };
  if (window.magicAddon) { // Check if my addon is installed
    magicAddon.doStuff(callbackSuccess, callbackError);
  }
}
unsafeWindow.magicAddon = {
  doStuff: function(callbackSuccess, callbackError) {
    // Bind the two callbacks to events. The addon will fire one of them
    self.port.on("doStuffSuccess", callbackSuccess);
    self.port.on("doStuffError", callbackError);

    // Fire the event that lets the addon do stuff
    self.port.emit("doStuff");
  }
};
这在第一次调用时效果很好,但下一次网站调用doStuff()时,新的侦听器会累加起来,并执行两次alert()。下次有三个警报,以此类推

你知道如何优雅地避免听众加起来吗?我可以完全清除事件类型吗?

到目前为止不起作用的是:

  • 改为使用
    self.port.once(…)
    ,因为我有两个回调事件:只有一个回调事件被清除,另一个回调事件保留并与下一个回调事件相加
  • 在注册新的侦听器之前,请使用
    self.port.removeListener
    删除旧的侦听器,因为我没有旧的回调引用

问题似乎与类似,只是他使用了一个回调侦听器,因此可以使用
self.port.once(..)
您可以使用
self.port.once
然后手动删除另一个回调:

doStuff: function(callbackSuccess, callbackError) {
  // Bind the two callbacks to events. The addon will fire one of them
  self.port.once("doStuffSuccess", function() {
    callbackSuccess();
    self.port.removeListener(callbackError);
  });
  self.port.once("doStuffError", function() {
    callbackError();
    self.port.removeListener(callbackSuccess);
  });

  // Fire the event that lets the addon do stuff
  self.port.emit("doStuff");
}
您处于内容脚本中,因此无法完全清除事件类型,只能在主附加代码中使用低级API进行清除

但是,我建议避免使用
unsafeWindow
来提供这种功能,因为它是不安全的。如果您保持API异步,您可以使用内容脚本和页面之间的
postMessage
管道来执行相同的操作;并提供了一个单独的javascript文件,人们可以将其包含在他们的网站中,您可以在其中公开postMessages调用的抽象(例如,
magicAddon.doStuff()
)。如果您愿意,您还可以在网站中自动从附加组件中插入该脚本

处理这个机制肯定有点复杂,但是可以避免使用
unsafeWindow

您可以找到有关内容脚本通信的更多信息

希望有帮助

更新:要回答您的评论,您需要一个变量来跟踪
doStuff
调用活动:

doStuff: function() {
  var executing = false;

  return function(callbackSuccess, callbackError) {
      if (executing)
        return;

      executing = true;

      // Bind the two callbacks to events. The addon will fire one of them
      self.port.once("doStuffSuccess", function() {
        executing = false;
        callbackSuccess();
        self.port.removeListener(callbackError);
      });
      self.port.once("doStuffError", function() {
        executing = false;

        callbackError();
        self.port.removeListener(callbackSuccess);
      });

      // Fire the event that lets the addon do stuff
      self.port.emit("doStuff");
  }
}()
注意末尾的
()
。基本上,在
doStuff
末尾设置的函数是我们最初分配的函数的结果。 通过这种方式,我们为
doStuff
方法创建一个闭包,其中一个
正在执行的
变量处于活动状态,如果已经有
doStuff
执行或没有执行,则保持跟踪,以丢弃任何其他
doStuff
调用,直到完成为止

注意:即使在这种情况下javascript不需要,也可以将该函数封装在括号中,以确定该函数是“自动执行的”:`doStuff:(function(){…}())


您也可以使用该代码的对象属性> MAGICADON < /代码>,但是在这种情况下,它将被暴露。

< P>您必须考虑在调用第一次调用之前,网站可能再次调用<代码> MAGICADON.DOSCODE()/代码>。因此,在任何时间点都可能有多个调用在执行——您应该确保调用正确的侦听器。此外,如果任何一个回调触发,您都需要删除这两个侦听器,否则您将泄漏内存。以下是这一方法的工作原理:

doStuff:function(callbackSuccess,callbackError){
//生成随机呼叫ID
var callID=Math.random();
//将两个回调绑定到事件。确保仅对具有
//右键呼叫ID。
函数onSuccess(id){
if(id==callID){
callbackSuccess();
RemovelListeners();
}
}
函数onError(id){
if(id==callID){
callbackError();
RemovelListeners();
}
}
函数RemovelListeners(){
self.port.removeListener(“dostuffsucture”,onSuccess);
self.port.removeListener(“dostufferro”,onError);
};
self.port.on(“dostuffsucture”,onSuccess);
self.port.on(“dostufferro”,onError);
//触发允许插件执行操作的事件
self.port.emit(“doStuff”,callID);
}

这使用随机调用ID来识别调用-代码处理
doStuff
事件获取调用ID作为参数,需要在
dostuffsucture
dostuferror
事件中将其发送回,以确保调用了正确的回调。

是的,
unsafeWindow
是不安全的,我已阅读,但是
document.body.appendChild(script)
对我来说似乎并没有太好…谢谢,这是一个好主意。然而,还有一个后续问题:doStuff具有用户交互功能,如果用户在其间吃午饭,它很容易会占用一个小时或更长的时间。当网站在不等待回调的情况下调用该函数两次时,两个侦听器都会同时得到两个回调,最终触发4个回调。你也有这个想法吗?我也很喜欢你的想法,谢谢。为什么您使用
self.port.removeListener(type,callback)
而其他人使用
self.port.removeListener(callback)
?两者都正确吗?@Doug:不,类型参数不是可选的,请参阅。这似乎只是ZER0提供的代码中的一个错误。