chrome.notification.create与Firefox WebExtension插件中的chrome.notification.onClicked

时间:2016-09-29 14:55:50

标签: javascript notifications onclick firefox-addon firefox-webextensions

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

最终,当您点击它时,我试图将通知的文本复制到剪贴板。但是,现在我在理解回调事件时遇到问题,或者我必须插入notification.onClicked函数。

目前,我不知道为什么{ "description": "Test Webextension", "manifest_version": 2, "name": "Σ", "version": "1.0", "permissions": [ "<all_urls>", "notifications", "webRequest" ], "background": { "scripts": ["background.js"] } } 听众什么都不做。

我的代码(将所有代码展示为WebExtension Firefox插件所需的代码):

的manifest.json

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

background.js

{{1}}

1 个答案:

答案 0 :(得分:2)

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

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

Firefox Bug:
您的主要问题是,如果您尝试过快地创建通知,则会遇到Firefox混乱的Firefox。因此,我已经实现了通知队列,并且速率限制了通知的创建。什么是#34;太快&#34;可能与操作系统和CPU有关,因此您最好在注意方面出错,并将调用chrome.notifications.create()之间的延迟设置为更高的值。在下面的代码中,延迟是500毫秒。我在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()侦听器中的通知文本时创建并单击通知。它没有实现关于将文本放入剪贴板的部分,因为实际上并没有在您的问题的代码中实现。我在代码中添加了自由派评论来解释发生了什么,并提供了相当多的console.log()输出来帮助显示正在发生的事情。我已经在Firefox Developer Edition(目前为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文档中的不兼容性。