问题描述
我在理解WebExtensions的文档时遇到了一个问题:事件。
最后,我试图获取通知的文本点击它时复制到剪贴板。不过,现在我有一个问题,理解回调的事情,或者我必须插入 notification.onClicked
函数。
目前,我不知道为什么 notification.onClicked
监听器没有任何作用。
我的代码(需要将所有代码演示为WebExtension Firefox附加组件):
$ b
清单.json
$ b
{
description:Test Webextension,
manifest_version :2,
name:Σ,
version:1.0,
permissions:[
< all_urls> ,
notifications,
webRequest
],
background:{
scripts:[background.js ]
code
$ b background.js 'use strict';
函数logURL(requestDetails){
notify(Testmessage);
chrome.notifications.onClicked.addListener(function(){
console.log(TEST TEST);
});
函数notify(notifyMessage){
var options = {
type:basic,
iconUrl:chrome.extension.getURL( icons / photo.png),
title:,
message:notifyMessage
};
chrome.notifications.create(ID123,options);
chrome.webRequest.onBeforeRequest.addListener(
logURL,{
url:[< all_urls>]
}
);
解决方案首先,您需要在Firefox 47.0 +,作为对是在版本47.0中添加的。虽然这可能不是你的问题,但它是一个可能性。
你的代码有多个问题。有些是在你的代码中,但主要是你遇到了Firefox的错误。
Firefox错误:
您的主要问题是,如果您遇到Firefox迷惑尝试快速创建通知。因此,我已经实施了通知队列,并且限制了通知的创建。什么是太快可能是操作系统和CPU的依赖,所以你最好在错误的方面谨慎,并设置调用之间的延迟更高。在下面的代码中,延迟是500ms。我在页面和 API。因此,你必须自己存储这些信息。下面的代码实现了一个Object来做到这一点,所以可以在 chrome.notifications.onClicked()
处理程序中访问文本。
示例代码:
下面的代码实现了我相信您的愿望。它只是创建并单击通知,同时访问 chrome.notifications.onClicked()
侦听器中的通知文本。它没有实现将文本放入剪贴板的部分,因为在问题的代码中并没有实际实现。我已经在代码中加入了自由注释来解释发生了什么,并提供了相当多的 console.log()
输出来帮助显示正在发生的事情。我已经在Firefox Developer Edition(目前为v51.0a2)和Google Chrome浏览器中进行了测试。
$ b background.js em> manifest.json ):
'use strict';
// *要进行测试,请打开浏览器控制台
var isFirefox = window.InstallTrigger?true:false;
尝试{
if(isFirefox){//仅在Firefox
中执行此操作// Firefox不支持警报。这强制浏览器控制台打开。
//在FF49.0b +中这种滥用错误的功能,而不是FF48
alert('打开浏览器控制台')。
catch(e){
// alert在Firefox版本低于49
console.log('Alert提示错误,可能Firefox版本低于49。 );
// *
//如果我们尝试快速创建通知(这是
// Firefox中的一个错误),Firefox会感到困惑。所以,对于Firefox,我们评估显示通知的限制。
//可能的最大速率(最小延迟)可能是操作系统和CPU的速度。
//因此,您应该小心谨慎,延迟时间更长。
//在Chrome中不需要延迟。
var notificationRateLimit = isFirefox? 500:0; // Firefox:每500M只有一个通知
var notificationRateLimitTimeout = -1; //超时通知速率限制
var sentNotifications = {};
var notificationsQueue = [];
var notificationIconUrl = chrome.extension.getURL(icons / photo.png);
函数logURL(requestDetails){
//console.log('webRequest.onBeforeRequest URL:'+ requestDetails.url);
//注意:在Chrome中,会发出一个webRequest来获取通知的图标。
//如果Chrome找到该图标,该图标的webRequest只发出两次。
//然而,如果图标不存在,那么这将建立一个无限循环,
//将以最大利用率固定一个CPU。
//因此,您不应该通知图标的URL。
//您应该考虑从您的
//自己的扩展名中通知所有网址。
if(requestDetails.url!== notificationIconUrl){
notify('webRequest URL:'+ requestDetails.url);
}
//您在问题中的原始代码:
//无条件地添加匿名notifications.onClicked侦听器
//这里会导致多行'TEST TEST'输出每次点击
//通知。您应该只添加侦听器一次。
}
函数notify(notifyMessage){
//将消息添加到通知队列。
notificationsQueue.push(notifyMessage);
console.log('Notification added to queue。message:'+ notifyMessage);
if(notificationsQueue.length == 1){
//如果这是队列中唯一的通知,发送它。
showNotificationQueueWithRateLimit();
}
//如果notificationsQueue有额外的条目,当前通知已经完成显示时,它们会显示
//。
如果(notificationRateLimitTimeout === - 1){
//没有当前的延迟激活,所以立即发送通知。
showNextNotification();
}
//如果有一个延迟激活,我们不需要做任何事情,因为通知
//在队列中被处理时将被发送。
}
function showNextNotification(){
notificationRateLimitTimeout = -1; //表示没有当前超时运行。
if(notificationsQueue.length === 0){
return; //队列中没有任何内容
}
//表示会有超时运行。
// Neeed,因为我们在notifications.create回调函数中设置了超时。
notificationRateLimitTimeout = -2;
//从队列中获取下一个通知
let notifyMessage = notificationsQueue.shift();
console.log('显示通知消息:'+ notifyMessage);
//设置我们的标准选项
let options = {
type:basic,
//如果图标不存在,Chrome会生成一个错误,但不会生成Firefox 。
//在Chrome中,生成一个webRequest来获取图标。因此,我们需要知道
// webRequest处理程序中的iconUrl,而不是通知该URL。
iconUrl:notificationIconUrl,
title:,
message:notifyMessage
};
//如果你想同时显示多个通知,你的消息ID必须是
//唯一的(至少在你的扩展中)。
//创建具有相同ID的通知会导致事先通知为
//销毁,而新创建的一个(不只是被替换的文本)。
//如果您一次只需要一个通知,请使用以下两行。如果你是
//实际上要通知每个webRequest(而不是这样做只是一个方式
//测试),你应该可能只有一个通知,因为他们将强奸
//关闭许多页面的屏幕。
// let myId ='ID123';
//chrome.notifications.create(myId,options,function(id){
//如果你想要多个通知而不必为每个通知创建一个唯一的ID
// //通过使用下面的行来为你创建ID:
chrome.notifications.create(options,function(id){
//在这个回调函数中,通知并不一定真的被显示,
//只是通知ID已经创建,通知在
//显示过程中。
console.log('Notification created,id ='+ id +': :message:'+ notifyMessage);
logIfError();
//记住文本,所以我们可以稍后得到它
sentNotifications [id] = {
message:notifyMessage
}
//在速率限制延迟后显示FIFO队列中的下一个通知
//为了启动延迟,这个被无条件地调用另一个
//通知要排队,即使现在不在队列中。
notificationRateLimitTimeout = setTimeout(showNextNotification
,notificationRateLimit);
});
函数logIfError(){
if(chrome.runtime.lastError){
let message = chrome.runtime.lastError.message;
console.log('Error:'+ message);
chrome.webRequest.onBeforeRequest.addListener(
logURL,{
url:[< all_urls>]
}
);
//添加notifications.onClicked匿名监听器只有一次:
//我个人认为使用一个命名函数
//在全局范围。这样做可以防止无意中多次添加
//。虽然,你的代码应该写成这样,你
//不这样做。
chrome.notifications.onClicked.addListener(function(id){
//我们不能从这里得到通知文本,只是ID。因此,我们
//必须使用
console.log('Clicked notification message text:',sentNotifications [id] .message);
//在Firefox中点击时会自动清除通知
//如果你想在Chrome中使用相同的功能,你需要清除()
//你自己:
//总是这样做,而不是只在不在Firefox的时候才能保持一致
//即使Firefox更改为与Chrome匹配
chrome.notifications.clear(id);
//这是我们使用通知文本的最后一个地方,所以我们将其删除
// sentNotifications,所以我们没有内存泄漏。
delete sentNotifications [id];
});
//直接测试通知,不需要webRequests:
notify('Background.js loaded');
通知('第二次通知');
在这个过程中,我发现Chrome和Firefox之间存在多个不兼容问题。我正在更新MDN以提及MDN文档中的不兼容性。
I have a problem understanding the documentation for the WebExtensions notification.onClicked
event.
Ultimately, I'm trying to get the text of the notification copied to the clipboard when you click on it. However, right now I am having a problem understanding the callback thing, or where I have to insert the notification.onClicked
function.
At the moment, I don't know why the notification.onClicked
listener does nothing.
My code (all the code needed to demonstrate the problem as a WebExtension Firefox add-on):
manifest.json
{
"description": "Test Webextension",
"manifest_version": 2,
"name": "Σ",
"version": "1.0",
"permissions": [
"<all_urls>",
"notifications",
"webRequest"
],
"background": {
"scripts": ["background.js"]
}
}
background.js
'use strict';
function logURL(requestDetails) {
notify("Testmessage");
chrome.notifications.onClicked.addListener(function() {
console.log("TEST TEST");
});
}
function notify(notifyMessage) {
var options = {
type: "basic",
iconUrl: chrome.extension.getURL("icons/photo.png"),
title: "",
message: notifyMessage
};
chrome.notifications.create("ID123", options);
}
chrome.webRequest.onBeforeRequest.addListener(
logURL, {
urls: ["<all_urls>"]
}
);
解决方案 First, you need to be testing this in Firefox 47.0+, as support for chrome.notifications.onClicked()
was added in version 47.0. While this is probably not your problem, it is one contributing possibility.
There are multiple issues with your code. Some are in your code, but primarily you are running into a Firefox bug.
Firefox Bug:
Your primary issue is that you are running into a Firefox bug where Firefox gets confused if you try to create notifications too rapidly. Thus, I have implemented a notification queue and rate limited the creation of notifications. What is "too rapidly" is probably both OS and CPU dependent, so you are best off erroring on the side of caution and set the delay between calls to chrome.notifications.create()
to a higher value. in the code below, the delay is 500ms. I have added a note regarding this issue in the chrome.notifications.create()
page on MDN and on the Chrome incompatibilities page.
Adding multiple copies of the same listener:
The main thing that you are doing wrong in your code is that you are adding an anonymous function as a listener, using chrome.notifications.onClicked.addListener()
, multiple times to the same event. This is a generic issue with event handlers. When you use an anonymous function it is a different actual function each time you are trying to add it, so the same functionality (in multiple identical functions) gets added multiple times. You should not be adding functions, which do the exact same thing, multiple times to the same event. Doing so is almost always an error in your program and results in unexpected operation.
In this case, the multiple functions would have ended up outputing multiple lines of TEST TEST
to the console each time the user clicked on a notification. The number of lines output per click would increase by one for each web request which resulted in a call to logURL
.
The way to prevent doing this is to be sure to add the listener only once. If you are using an anonymous function, you can only do this by being sure you only execute the addListener
(or addEventlistener
) once (usually by only adding the listener from your main code (not from within a function), or from a function that is only called once. Alternately, you can name/define your listener function directly within the global scope (or other scope accessible to all places where you try to add the listener) (e.g. function myListener(){...}
). Then, when you are adding myListener
you are always referring to the same exact function which JavaScript automatically prevents you from adding in the same way to the same event more than once.
It should be noted that if you are trying to add a anonymous function as a listener from another listener, you are almost always doing something wrong. Adding copies of identical anonymous listeners multiple times to the same event is a common error.
Access to the notification text:
While you do not implement anything regarding using the text of the notification, you state that you want to add the text of the notification to the clipboard when the user clicks on the notification. You can not obtain the notification text from any portion of the chrome.notifications
API. Thus, you have to store that information yourself. The code below implements an Object to do that so the text can be accessed in the chrome.notifications.onClicked()
handler.
Example code:
The code below implements what I believe you desire. It is just creating and clicking the notification while having access to the notification text in the chrome.notifications.onClicked()
listener. It does not implement the part about putting the text into the clipboard, as that was not actually implemented in the code in your Question. I have added liberal comments to the code to explain what is happening and provided quite a bit of console.log()
output to help show what is going on. I have tested it in both Firefox Developer Edition (currently v51.0a2) and Google Chrome.
background.js (no changes to your manifest.json):
'use strict';
//* For testing, open the Browser Console
var isFirefox = window.InstallTrigger?true:false;
try{
if(isFirefox){ //Only do this in Firefox
//Alert is not supported in Firefox. This forces the Browser Console open.
//This abuse of a misfeature works in FF49.0b+, not in FF48
alert('Open the Browser Console.');
}
}catch(e){
//alert throws an error in Firefox versions below 49
console.log('Alert threw an error. Probably Firefox version below 49.');
}
//*
//Firefox gets confused if we try to create notifications too fast (this is a bug in
// Firefox). So, for Firefox, we rate limit showing the notifications.
// The maximum rate possible (minimum delay) is probably OS and CPU speed dependent.
// Thus, you should error on the side of caution and make the delay longer.
// No delay is needed in Chrome.
var notificationRateLimit = isFirefox ? 500:0;//Firefox:Only one notification every 500m
var notificationRateLimitTimeout=-1; //Timeout for notification rate limit
var sentNotifications={};
var notificationsQueue=[];
var notificationIconUrl = chrome.extension.getURL("icons/photo.png");
function logURL(requestDetails) {
//console.log('webRequest.onBeforeRequest URL:' + requestDetails.url);
//NOTE: In Chrome, a webRequest is issued to obtain the icon for the notification.
// If Chrome finds the icon, that webRequest for the icon is only issued twice.
// However, if the icon does not exist, then this sets up an infinite loop which
// will peg one CPU at maximum utilization.
// Thus, you should not notify for the icon URL.
// You should consider excluding from notification all URLs from within your
// own extension.
if(requestDetails.url !== notificationIconUrl ){
notify('webRequest URL: ' + requestDetails.url);
}
//Your Original code in the Question:
//Unconditionally adding an anonymous notifications.onClicked listener
// here would result in multiple lines of 'TEST TEST' ouput for each click
// on a notification. You should add the listener only once.
}
function notify(notifyMessage) {
//Add the message to the notifications queue.
notificationsQueue.push(notifyMessage);
console.log('Notification added to queue. message:' + notifyMessage);
if(notificationsQueue.length == 1){
//If this is the only notification in the queue, send it.
showNotificationQueueWithRateLimit();
}
//If the notificationsQueue has additional entries, they will get
// shown when the current notification has completed being shown.
}
function showNotificationQueueWithRateLimit(){
if(notificationRateLimitTimeout===-1){
//There is no current delay active, so immediately send the notification.
showNextNotification();
}
//If there is a delay active, we don't need to do anything as the notification
// will be sent when it gets processed out of the queue.
}
function showNextNotification() {
notificationRateLimitTimeout=-1; //Indicate that there is no current timeout running.
if(notificationsQueue.length === 0){
return; //Nothing in queue
}
//Indicate that there will be a timeout running.
// Neeed because we set the timeout in the notifications.create callback function.
notificationRateLimitTimeout=-2;
//Get the next notification from the queue
let notifyMessage = notificationsQueue.shift();
console.log('Showing notification message:' + notifyMessage);
//Set our standard options
let options = {
type: "basic",
//If the icon does not exist an error is generated in Chrome, but not Firefox.
// In Chrome a webRequest is generated to fetch the icon. Thus, we need to know
// the iconUrl in the webRequest handler, and not notify for that URL.
iconUrl: notificationIconUrl,
title: "",
message: notifyMessage
};
//If you want multiple notifications shown at the same time, your message ID must be
// unique (at least within your extension).
//Creating a notification with the same ID causes the prior notification to be
// destroyed and the new one created in its place (not just the text being replaced).
//Use the following two lines if you want only one notification at a time. If you are
// actually going to notify on each webRequest (rather than doing so just being a way
// to test), you should probably only have one notification as they will rapedly be
// off the screen for many pages.
//let myId = 'ID123';
//chrome.notifications.create(myId,options,function(id){
//If you want multiple notifications without having to create a unique ID for each one,
// then let the ID be created for you by using the following line:
chrome.notifications.create(options,function(id){
//In this callback the notification has not necessarily actually been shown yet,
// just that the notification ID has been created and the notification is in the
// process of being shown.
console.log('Notification created, id=' + id + ':: message:' + notifyMessage);
logIfError();
//Remember the text so we can get it later
sentNotifications[id] = {
message: notifyMessage
}
//Show the next notification in the FIFO queue after a rate limiting delay
// This is called unconditionally in order to start the delay should another
// notification be queued, even if one is not in the queue now.
notificationRateLimitTimeout = setTimeout(showNextNotification
,notificationRateLimit);
});
}
function logIfError(){
if(chrome.runtime.lastError){
let message =chrome.runtime.lastError.message;
console.log('Error: ' + message);
}
}
chrome.webRequest.onBeforeRequest.addListener(
logURL, {
urls: ["<all_urls>"]
}
);
//Add the notifications.onClicked anonymous listener only once:
// Personally, I consider it better practice to use a named function that
// is defined in the global scope. Doing so prevents inadvertantly adding
// it multiple times. Although, your code should be written such that you
// don't do that anyway.
chrome.notifications.onClicked.addListener(function(id) {
//We can not get the notification text from here, just the ID. Thus, we
// have to use the text which was remembered.
console.log('Clicked notification message text: ', sentNotifications[id].message);
//In Firefox the notification is automatically cleared when it is clicked.
// If you want the same functionality in Chrome, you will need to clear() it
// yourself:
//Always do this instead of only when not in Firefox so that it remains consistent
// Even if Firefox changes to match Chrome.
chrome.notifications.clear(id);
//This is the last place we use the text of the notification, so we delete it
// from sentNotifications so we don't have a memory leak.
delete sentNotifications[id];
});
//Test the notifications directly without the need to have webRequests:
notify('Background.js loaded');
notify('Second notification');
In the process of working on this, I found multiple incompatibilities between Chrome and Firefox. I am in the process of updating MDN to mention the incompatibilities in the documentation on MDN.
这篇关于chrome.notification.create with chrome.notification.on在Firefox WebExtension插件中单击的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!