javascript - 使用chrome.notification.on创建chrome.notification.create在Firefox WebExtension插件中单击

标签 javascript notifications onclick firefox-addon firefox-webextensions

我在理解WebExtensions notification.onClicked事件的文档时遇到问题。

最终,我要在单击通知时将通知文本复制到剪贴板。但是,现在我在理解回调内容或必须在其中插入notification.onClicked函数的地方遇到了问题。

目前,我不知道为什么notification.onClicked侦听器什么也不做。

我的代码(作为WebExtension Firefox附加组件演示问题所需的所有代码):

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

最佳答案

首先,您需要在Firefox 47.0+中对此进行测试,因为在版本47.0中添加了对chrome.notifications.onClicked()的支持。尽管这可能不是您的问题,但这是一种可能。

您的代码有多个问题。其中一些在您的代码中,但主要是您遇到了Firefox错误。

Firefox错误:
您的主要问题是您遇到了一个Firefox错误,如果您尝试过快地创建通知,Firefox会感到困惑。因此,我实现了一个通知队列,并限制了通知的创建速度。 “太快”的情况可能与操作系统和CPU都有关,因此,最好还是避免出现错误,并把对chrome.notifications.create()的调用之间的延迟设置为更高的值。在下面的代码中,延迟为500ms。我在MDN的chrome.notifications.create()页和Chrome incompatibilities页上添加了有关此问题的注释。

添加同一侦听器的多个副本:
您在代码中做错的主要事情是,您多次使用chrome.notifications.onClicked.addListener()向同一事件添加一个匿名函数作为侦听器。这是事件处理程序的一般性问题。当您使用匿名函数时,每次尝试添加匿名函数时,它都是一个不同的实际函数,因此相同的功能(在多个相同的函数中)会被多次添加。您不应该向同一事件添加多次执行完全相同的操作的函数。这样做几乎总是程序中的错误,并会导致意外操作。

在这种情况下,每次用户单击通知时,多个功能最终都会向控制台输出多行TEST TEST。对于每个Web请求,每次点击输出的行数将增加一,从而导致调用logURL

防止这样做的方法是确保仅添加一次侦听器。如果您使用的是匿名函数,则只能通过确保只执行一次addListener(或addEventlistener)一次来完成此操作(通常只从主代码中添加侦听器(而不是从函数内部),或者或者,您可以直接在全局作用域(或尝试添加侦听器的所有地方都可以访问的其他作用域)中命名/定义侦听器函数(例如,function myListener(){...})。您要添加myListener时,始终指的是相同的函数,JavaScript会自动阻止您以相同的方式多次向同一事件添加相同的函数。

应该注意的是,如果您试图从另一个侦听器中添加一个匿名函数作为侦听器,则几乎总是在做错事。多次将相同匿名侦听器的副本添加到同一事件是一个常见错误。

访问通知文本:
尽管您没有实现有关使用通知文本的任何内容,但是您声明要在用户单击通知时将通知文本添加到剪贴板。您无法从chrome.notifications API的任何部分获取通知文本。因此,您必须自己存储该信息。下面的代码实现了一个Object来执行此操作,因此可以在chrome.notifications.onClicked()处理程序中访问文本。

示例代码:
下面的代码实现了我认为您想要的东西。它只是创建并单击通知,同时可以访问chrome.notifications.onClicked()侦听器中的通知文本。它没有实现有关将文本放入剪贴板的部分,因为您的Question中的代码并未实际实现。我在代码中添加了自由注释,以解释正在发生的事情,并提供了大量的console.log()输出以帮助显示正在发生的事情。我已经在Firefox开发人员版(当前为v51.0a2)和Google Chrome中对其进行了测试。

background.js(未更改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');


在解决此问题的过程中,我发现Chrome和Firefox之间存在多个不兼容问题。我正在更新MDN,以提及有关MDN的文档中的不兼容性。

关于javascript - 使用chrome.notification.on创建chrome.notification.create在Firefox WebExtension插件中单击,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39773656/

相关文章:

javascript - 单击幻灯片时播放视频

javascript - 缓存javascript文件的基础是什么?

javascript - 在 angularJS 中集成验证码的最佳方法

c# - 使用azure向IOS和android发送广播通知

c# - OneSignal 推送通知 ClickEvent 显示空值 Windows Phone 8.1 C#

javascript - 使用 onclick 创建元素

javascript - Chrome 开发工具 Service Worker 错误图标/计数

javascript - 使用 UDP 的 Node.js 文件传输

来自服务的 Android 通知未打开 Activity

jquery - 单击 div 外部关闭 JQuery 图像