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



我的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");
}
};

这在第一次调用时效果很好,但下次网站调用doStuff()时,新的侦听器会相加,alert()会执行两次。下次三次警报,依此类推。

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

到目前为止没有奏效的地方:

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

问题似乎类似于如何删除事件侦听器?,只是他使用了一个回调侦听器,因此可以使用CCD_ 3

您可以使用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方法创建了一个闭包,其中executing变量是活动的,并且如果已经执行了doStuff或没有执行,则保持跟踪,以便丢弃任何其他doStuff调用,直到完成为止。

注意:即使在这种情况下javascript不是必需的,也可以很好地将该函数包装在括号中,以确定该函数是"自执行的":`doStuff:(function(){…}())

您也可以将对象的属性magicAddon用于此作业,但在这种情况下,它将被公开。

在收到第一次呼叫的响应之前,您必须考虑网站可能再次呼叫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事件中将其发回,以确保调用正确的回调。

最新更新