2012-12-11 49 views
2

我的Firefox扩展提供了JavaScript功能,网站可以使用该功能访问附加功能。网站调用这个函数并提供两个回调。Firefox扩展(SDK)中的内容脚本添加了事件侦听器

网站代码:

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(),新侦听器添加up和alert()会执行两次。下一次三次警报,等等。

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

至今没有制定什么:

  • 使用self.port.once(..)代替,因为我有两个回调事件:只有回击被清除,另外一个保持,而且与下增加了一个。
  • 在注册新的侦听器之前,用self.port.removeListener删除旧的侦听器,因为我没有旧的回调引用。

问题似乎类似于How to remove an event listener?,只能说他使用一个回调监听,因此可以使用self.port.once(..)

+0

是的,'unsafeWindow'是不安全的,我读过http://bit.ly/rJbtA2,但是'document.body.appendChild(script)'对我来说好像不太好... – Hannobo

回答

1

您可以使用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

你可以找到更多关于内容脚本通信here

希望它有帮助!

更新:要在您的评论回答,您需要一个变量来跟踪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方法创建一个闭包,其中一个executing变量处于活动状态,如果已有doStuff执行或不存在,则保留跟踪,以便放弃任何其他doStuff调用,直至完成。

注意:即使在这种情况下对于javascript来说不是必需的,也可能是一个很好的约定包装函数的括号,以确定该函数是“自动执行”的:doStuff:(function(){.. ())

您也可以使用magicAddon的对象的属性来完成这项工作,但在这种情况下,它将被暴露。

+0

谢谢这是一个很好的主意。但是,有一个后续问题:doStuff具有用户交互功能,如果用户在中间进行午餐,则可能需要长达一个小时或更长的时间。当网站两次调用函数而不等待回调函数时,*两个*侦听器都会得到两个回调函数,最终导致4个回调函数被触发。对此也有任何想法? – Hannobo

1

您必须考虑在收到第一个电话的回复之前网站可能再次拨打magicAddon.doStuff()的可能性。因此,可能有多个呼叫在任何时间点执行 - 并且您应该确保呼叫正确的听众。另外,如果任何一个回调触发,则需要删除两个侦听器,否则会泄漏内存。下面是这可能是工作:

doStuff: function(callbackSuccess, callbackError) { 
    // Generate a random call ID 
    var callID = Math.random(); 

    // Bind the two callbacks to events. Make sure to only act on events with the 
    // right call ID. 
    function onSuccess(id) { 
    if (id == callID) { 
     callbackSuccess(); 
     removeListeners(); 
    } 
    } 
    function onError(id) { 
    if (id == callID) { 
     callbackError(); 
     removeListeners(); 
    } 
    } 
    function removeListeners() { 
    self.port.removeListener("doStuffSuccess", onSuccess); 
    self.port.removeListener("doStuffError", onError); 
    }; 
    self.port.on("doStuffSuccess", onSuccess); 
    self.port.on("doStuffError", onError); 

    // Fire the event that lets the addon do stuff 
    self.port.emit("doStuff", callID); 
} 

这使用随机呼叫ID来识别来电 - 代码处理doStuff事件得到一个呼叫ID为参数,需要将其发送回在doStuffSuccessdoStuffError事件,以确保正确的回调被称为。

+0

也喜欢你的想法,谢谢。你为什么使用'self.port.removeListener(type,callback)'而其他人使用'self.port.removeListener(callback)'?都正确吗? – Hannobo

+0

@Doug:不,“type”参数不是可选的,参见[documentation](https://addons.mozilla.org/en-US/developers/docs/sdk/1.13/modules/sdk/event/target html的#的removeListener%28type%2C%20listener%29)。这似乎只是ZER0提供的代码中的一个错误。 –

相关问题