本文介绍了如何在Firefox WebExtensions加载项中使用chrome.storage和runtime.connect的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述




  • 发送消息给后台脚本,当
  • 发送的消息由后台脚本存储

  • 存储的消息由后台脚本检索

  • 单击加载项浏览器按钮时,存储的消息将从后台脚本发送到内容脚本
  • 内容脚本显示接收到的消息


我正在使用Firefox版本48的Windows,并且无法启动它。请问有人指出我在做什么错误请。



这是我的内容脚本:

  / b> 

函数gotMessage(msg)
{
//存储消息
chrome.storage.local.set({message:msg.txt},function(){
//再次读取存储的消息
chrome.storage.local.get(message ,函数(item){
tmp = item;
//表示数据是可用的
//只有在需要的时候才会这样做
//chrome.browserAction.setTitle ({title:'发送数据到内容脚本'));
});
});





注意:在你的问题中也有一个语法错误,其中 frunction gotMessage(msg)应该是函数gotMessage(msg)。

storage.local 可用于内容脚本:

您当前正在将消息传递到内容脚本和后台脚本为了 set()和 get() storage.local的内容在您的后台脚本中。我假设你正在测试 runtime.connect , browser_action 等。另一种可能性是你不知道你可以用 storage.local 和 set c>从您的内容脚本。

全功能代码:

还有其他多个问题。大部分问题是由于用户需要使用一些复杂的动作序列才能使其工作。一些需要的是由于WebExtensions / Firefox的一些特殊性:


  1. 完全删除以前安装的旧版本的加载项
    $ b


    1. 从 about:addons ( - , - - 在OSX上),移除 。

    2. 刷新所有内容页面(或者至少您正在测试的任何内容页面)。这将从以前的安装中删除任何注入的内容脚本。在删除/禁用插件时,Firefox自动删除内容脚本。


  2. 重新加载临时插件 about:debugging 。

  3. 刷新您正在测试的页面。这是需要的,因为当加载项被重新加载时,内容脚本首先被注入。在内容脚本中,您可以。如果不是第一个 ,则不会连接侦听器。因此,正如内容脚本在后台脚本之后加载的那样。

  4. 点击内容页面

  5. 点击 browser_action 按钮

不需要执行正是这个顺序,很难得到它做你想做的事情。



困难的一个重要部分是弄清楚是怎么回事有相当数量的国家信息只包含在代码中。没有迹象表明内容脚本或后台脚本处于什么状态。为了帮助可视化每个脚本中发生的事情,我添加了大量的调用 console.log 以更好地说明脚本中发生了什么。



我已经大幅修改了代码:


  1. 尝试发送消息时(由于被点击的页面)认识到没有建立连接,并再次尝试获取连接(结果:否在上面的用户序列中需要更长的时间执行#3)。
  2. 如果检测到有连接并丢失了连接,则自动禁用。这种情况最可能的原因是受影响的内容脚本被重新加载的附件所孤立。 (结果:不再需要在上面的序列中执行#1;可以在不移除旧版本/重新加载内容页面的情况下重新加载附加组件。)
  3. 还有其他问题在代码中。我相信我在代码中添加了足够的注释来解释它们。

现在在浏览器控制台中生成的输出是: p>

1471884197092 addons.xpi标识为WARN的插件demo-runtime.connect-and-storage.local@example .com已安装,旧版本将被禁用
内容:注入的内容脚本。
content:使消息端口可用于连接
alert()在后台窗口中不被支持;请改用console.log。
打开浏览器控制台。
背景:在后台脚本中。
background:监听连接
content:点击页面
发送消息失败:尚未连接
content:重试消息连接:Object {type:page clicked }
content:使消息端口可用于连接
background:端口连接:发送确认消息:Object {type:消息通道建立}
content:收到消息:type:message channel established
content:发送挂起消息Object {type:page clicked}
content:发送消息Object {type:page clicked}
background:接收消息:Object {type:page点击}
背景:获取来自storage.local.get的数据:Object {message:page clicked}
背景:点击按钮发送消息:Object {type:saved message,data:页面点击}
内容:收到消息:类型:保存消息
消息:数据:页面c舔了

manifest.json
$
name:Demo runtime.connect and storage.local,
manifest_version:b $ b

  2,
version:0.1,
permissions:[activeTab,storage],
content_scripts:[
{
matches:[< all_urls>],
js:[content.js]
}
],

background :{
scripts:[background.js]
},

browser_action:{
default_title:Data not yet available ,
browser_style:true
},

applications:{
gecko:{
id:demo-runtime .connect-and-storage.local @ example.com,
strict_min_version:48.0a1
}
}
}








$ b $ prefb =lang- js prettyprint-overri为了进行测试,请打开浏览器控制台
尝试{
// Firefox中的警报不受支持。这强制浏览器控制台打开。
//这个滥用错误的功能在FF49.0b +中,而不是在FF48
alert('打开浏览器控制台')。
} catch(e){
// alert在Firefox版本低于49
console.log('Alert throws an error。

// *
console.log('background:In background script。');

var msgPort;
var dataFromStorage;

//当消息端口连接到这个后台脚本时触发
函数连接(prt){
msgPort = prt;
msgPort.onMessage.addListener(gotMessage); //这应该先完成
let message = {type:message channel established};
console.log('background:Port connected:sending confirmation message:',message);
msgPort.postMessage(message);


//当内容脚本发送消息时触发
//语法error this line(拼写错误的函数)
函数gotMessage(msg){
console.log('background:Received message:',msg);
//存储消息
chrome.storage.local.set({message:msg.type},function(){
//再次读取存储的消息
chrome .storage.local.get(message,function(item){
console.log('background:从storage.local.get获得数据:',item);
//你正在设置tmp(现在是dataFromStorage)到item对象,而不是你所要求的
//消息键
/ *
for(x in item){
console.log('背景:项目的属性:',x);
}
// * /
dataFromStorage = item.message;
//向用户指示数据可用
chrome.browserAction.setTitle({title:'发送数据到内容脚本'});
});
});
}

//单击附加按钮时将保存的消息发送到内容脚本
chrome.browserAction.onClicked.addListener(function(){$ b $ (msgPort){
let message = {type:saved message,data:dataFromStorage};
console.log('msgPort'); // msgPort未定义,除非用户点击页面
if背景:点击按钮发送消息:',message);
msgPort.postMessage(message);
} else {
console.log('background:没有可用的消息端口(尚)。 );
}
});

//建立连接。这必须在代码中其他地方的
// chrome.runtime.connect之前完成。
chrome.runtime.onConnect.addListener(connected);
console.log('background:Listening for a connection');

content.js

  console.log('\tcontent:Content script inject。'); 

var isConnected = false;
var retryConnectionTimerId = -1; //如果我们想取消
var retryConnectionCount = 0;
var messageBeingRetried = null;

//在此脚本和后台脚本之间设置消息通道
var msgPort;

函数messageListener(msg){
//对这个函数使用闭包是一个坏主意。这应该是一个在全局范围定义的名为
的函数,所以如果后台脚本发送消息断开连接,我们可以将其作为
//监听器移除。
//如果禁用/删除
//附加组件,您需要禁用任何活动的内容脚本。这是Mozilla的政策。但是,
//对于WebExtensions而言,由于当前缺少
// runtime.onSuspend事件,所以还不可能。
if(typeof msg ==='object'&& msg.hasOwnProperty('type')){
//应特别注意指示连接的消息。
console.log('\tcontent:Received message:type:',msg.type
,(msg.hasOwnProperty('data')?'\\\
\t\t\ t消息:data:':'')
,(msg.hasOwnProperty('data')?msg.data:'')
);
if(msg.type ==='disableAddon'){
//允许后台脚本禁用加载项。
disableThisScript('Received disableAddon message');

if(isConnected& msg.type ==='message channel established'){
//我们在一个内容脚本中, b $ b //此附加组件。或者,至少这是测试时最可能的
//。这可能需要改变一个真正的附加。
//这是因为重新加载临时加载项不
//自动禁用任何内容脚本。
disableThisScript('收到第二个通道建立的消息');
return;
} // else
isConnected = true; //接收到的格式正确的消息表示连接
//立即发送一条挂起消息(正在重试)。
//注意:这只能立即发送最近尝试
//的消息通过sendMessage发送,而不是所有可能在定时器中等待的消息。
//当其定时器到期时,将发送其他任何人。
sendPendingMessageIfPending();
} else {
console.log('\tcontent:收到的消息没有type:',msg);


$ b函数receiveDisconnect(){
//端口被断开
disableThisScript('Port disconnected');
isConnected = false;

$ b $函数makePortAvailableForConnection(){
console.log('\tcontent:使消息端口可用于连接');
if(msgPort& typeof msgPort.disconnect ==='function'){
//调用disconnect(),如果我们已经尝试了连接
msgPort.disconnect ();
}
//尝试建立连接。仅当ocConnect监听器
//已经建立时才有效。
msgPort = chrome.runtime.connect({name:msgPort});
//当后台脚本发送消息时触发
msgPort.onMessage.addListener(messageListener);
msgPort.onDisconnect.addListener(receiveDisconnect);
//无法使用runtime.onConnect检测到我们已连接。
//只有当其他脚本试图连接
//(使用chrome.runtime.connect或chrome.tabs.connect)
//到这个脚本(或者一般情况下) 。当此脚本启动连接
//时,它不会触发。
chrome.runtime.onConnect.addListener(portConnected); //不会触发


函数portConnected(){
//当连接启动
// chrome.runtime时,这个事件不会触发。连接()从这个脚本。
//这段代码留在这个例子中,并且证明事件
//不会触发。
console.log('\tcontent:Received onConnect event');
isConnected = true;


//单击页面时向后台脚本发送消息
函数sendClickMessage(){
console.log('\tcontent:Page clicked' );
sendMessage({type:page clicked});
chrome.storage.local.get(message,function(item){
console.log('content:从storage.local.get获取数据:',item);
});


$ b函数clearPendingMessage(){
window.clearTimeout(retryConnectionTimerId);
messageBeingRetried = null;
}

函数sendPendingMessageIfPending(){
//待处理的消息应该被实现为每个消息的队列
//被重试X次,然后发送一次建立连接。现在
//这适用于单个消息。任何等待
//的其他消息只会在重试期间待处理,然后被遗忘。
if(messageBeingRetried!== null&& retryConnectionTimerId){
let message = messageBeingRetried;
clearPendingMessage();
console.log('\tcontent:去发送挂起的消息',消息);
sendMessage(message);



函数retryingMessage(消息){
retryConnectionTimerId = -1;
messageBeingRetried = null;
sendMessage(message);

$ b函数sendMessage(消息){
if(isConnected){
try {
console.log('\tcontent:'发送消息' , 信息);
msgPort.postMessage(message);
retryConnectionCount = 0; (e.message.indexOf('disconnected port')> -1){
console.log('\tcontent:发送消息失败:
} catch(e){
if断开端口');
if(isConnected){
console.log('\tcontent:有连接,但是丢失了。'
+'可能加载重载,所以禁用');
disableThisScript('Add-on likely reloaded。');
} else {
retryConnection(message);

} else {
console.log('\tcontent:发送消息失败:未知错误',e);
}
}
} else {
console.log('\tcontent:发送消息失败:尚未连接');
retryConnection(message);



函数retryConnection(message){
if(retryConnectionCount> = 5){
//限制重试次数连接。
//如果现在还没有建立连接,可能根本就不是
//。不要用很多
//消息来填充控制台,这可能会使得难以看到发生了什么。
retryConnectionCount = 0; / /允许更多的重试另一个点击事件。
//当前消息被遗忘。现在只是丢弃。
return;
}
console.log('\tcontent:重试消息的连接:',消息);
makePortAvailableForConnection();
//尝试在超时后发送消息。
//这将导致重复尝试
//连接并发送消息。
messageBeingRetried = message;
retryConnectionTimerId = window.setTimeout(retryingMessage,500,message);
retryConnectionCount ++;


function disableThisScript(reason){
console.log('\tcontent:Disable the content script:',reason);
//优雅地禁用以前设置的一切。
msgPort.onMessage.removeListener(messageListener);
msgPort.onDisconnect.removeListener(receiveDisconnect);
尝试{
msgPort.disconnect();
} catch(e){
//最有可能的是端口已经被断开连接
}
chrome.runtime.onConnect.removeListener(portConnected);
document.body.removeEventListener(click,sendClickMessage);
isConnected = false;
}

//如果后台脚本中没有
// onConnect侦听器,则使连接可用将失败。在加载或重新加载临时
//附加的情况下,内容脚本首先运行,
//不建立连接。
makePortAvailableForConnection();

document.body.addEventListener(click,sendClickMessage);

注意:

这适用于演示或学习,但在生产扩展中使用效果不佳。代码中不包含多个标签。



您正在通过单击 browser_action将消息发送到内容脚本按钮。这意味着您需要能够将消息从后台脚本显式发送到用户单击 browser_action 按钮。例如,用户可能在一个页面中,单击内容,然后切换到另一个选项卡,然后单击 browser_action 按钮。在这种情况下,消息应该被发送到当前活动的选项卡,而不是被切换开(从而建立连接)的那个。



虽然你可以跟踪当你收到连接或点击消息(这可能是因为连接实际发生,或发送消息,基于点击事件(假定是用户输入))时,该标签是活动的,使用它可能会更好,它允许你只建立一个特定的选项卡连接。了解哪个对应于每个标签,可以确保您只将消息发送到当前 browser_action 按钮处于活动状态的选项卡点击。您需要保留一个数组或对象,其中包含由选项卡ID索引的已连接端口。


I'm trying to create a Firefox add-on that does the following:

  • sends a message to a background script when the page body is clicked
  • the sent message is stored by the background script
  • the stored message is retrieved by the background script
  • when the add-in browser button is clicked that stored message is sent from the background script to the content script
  • the content script displays the received message

I'm using Firefox version 48 for windows and I can't get it to work. Can someone point out what I'm doing wrong please.

Here is my content script:

// setup message channel between this script and background script
var msgPort = chrome.runtime.connect({name:"msgPort"});

// fires when background script sends a message
msgPort.onMessage.addListener(function(msg) {
    console.log(msg.txt);
});

// sends a message to background script when page is clicked
document.body.addEventListener("click", function() {
    msgPort.postMessage({txt: "page clicked"});
});

Here is my background script:

var msgPort;
var tmp;

// fires when message port connects to this background script
function connected(prt)
{
    msgPort = prt;
    msgPort.postMessage({txt: "message channel established"});
    msgPort.onMessage.addListener(gotMessage);
}

// fires when content script sends a message
frunction gotMessage(msg)
{
    // store the message
    chrome.storage.local.set({message : msg.txt});

    // read the stored message back again
    chrome.storage.local.get("message", function(item){
        tmp = item;
    });
}

// send the saved message to the content script when the add-on button is clicked
chrome.browserAction.onClicked.addListener(function() {
    msgPort.postMessage({txt: "saved message: "+tmp});
});

chrome.runtime.onConnect.addListener(connected);

And here is my manifest:

{
    "name": "test",

    "manifest_version": 2,

    "version": "1.0",

    "permissions": ["activeTab","storage"],

    "content_scripts": [
        {
            "matches": ["<all_urls>"],
            "js": ["content.js"]
        }
    ],

    "background": {
        "scripts": ["background.js"]
    },

    "browser_action": {
        "default_title": "Go"
    },

    "applications": {
        "gecko": {
        "id": "test@example.com",
        "strict_min_version": "48.0a1"
        }
    }
}
解决方案

While there may be other issues elsewhere in your code, one issue is that you are not accounting for the fact that chrome.storage.local.set is asynchronous.

As your code is, your call to chrome.storage.local.get is being executed immediately after your request to store data via chrome.storage.local.set without waiting for the data to actually be stored. Thus, the data may not yet be available.

Your gotMessage(msg) function should look more like:

function gotMessage(msg)
{
    // store the message
    chrome.storage.local.set({message : msg.txt}, function(){
        // read the stored message back again
        chrome.storage.local.get("message", function(item){
            tmp = item;
            //Indicate that the data is available
            //Only do this if you desire.
            //chrome.browserAction.setTitle({title:'Send data to content script'});
        });
    });
}

Note: there is also a syntax error in your question where frunction gotMessage(msg) should be function gotMessage(msg).

storage.local is available to content scripts:
You are currently passing messages back and forth to between your content script and background script in order to set() and get() the contents of storage.local within your background script. I am assuming that you are doing that to test runtime.connect, browser_action, etc.. Another possibility is that you are not aware that you can get and set with storage.local from your content script.

Fully functional code:

There were also multiple other issues. Most of the issues were the result of either the somewhat complex sequence of actions that was needed on the part of the user to get it to work. Some of what was required is due to some peculiarities of WebExtensions/Firefox.:

  1. Fully remove any old version of the add-on previously installed

    1. From about:addons (--, -- on OSX), "remove" the add-on.
    2. Refresh all content pages (or at least any on which you are testing). This removes any injected content scripts from the previous installation. Content scripts are not removed automatically by Firefox when the add-on is removed/disabled.

  2. Reload the temporary add-on from about:debugging.
  3. Refresh the page on which you were testing. This was needed because when the add-on was re-loaded, the content script was injected first. In the content script, you do runtime.connect(). This will not connect if there is not first a runtime.onConnect listener. Thus, as it was the content script had to be loaded after the background script.
  4. Click on the content page
  5. Click on the browser_action button`

Without knowing that you needed to perform exactly this sequence, it is difficult to get it to do what you wanted it to do.

A significant part of the difficulty in figuring out what was going on is that you had a considerable amount of state information which was only contained within the code. No indications were being made to the user as to what state the content script, or the background script were in. In order to help visualize what is happening in each of the scripts, I have added a significant number of calls to console.log to better illustrate what is happening within your scripts.

I have significantly modified the code:

  1. When trying to send a message (due to the page being clicked) recognize that a connection has not been made, and try again to obtain a connection (Result: No longer need to do #3 in the user sequence above).
  2. Disables itself if it detects it had a connection and lost it. The most likely cause of that circumstance is that the affected content script was orphaned by the add-on being reloaded. (Result: No longer need to do #1 in the sequence above; can re-load the add-on without removing the old version/reloading content pages first.)
  3. There were other issues in the code. I believe I added comments to the code sufficient to explain them.

The output that is now generated in the Browser Console is:

1471884197092 addons.xpi WARN Addon with ID demo-runtime.connect-and-storage.local@example.com already installed, older version will be disabled
        content: Content script injected.
        content: Making message port available for connection
alert() is not supported in background windows; please use console.log instead.
Open the Browser Console.
background: In background script.
background: listening for a connection
        content: page clicked
        content: Sending message failed: not yet connected
        content: Retrying connection for message: Object { type: "page clicked" }
        content: Making message port available for connection
background: Port connected: sending confirmation message: Object { type: "message channel established" }
        content: Received message: type: message channel established  
        content: Sending pending message Object { type: "page clicked" }
        content: Sending message Object { type: "page clicked" }
background: Received message: Object { type: "page clicked" }
background: Got data from storage.local.get: Object { message: "page clicked" }
background: Button clicked sending message: Object { type: "saved message", data: "page clicked" }
        content: Received message: type: saved message 
                          message: data: page clicked

manifest.json:

{
    "name": "Demo runtime.connect and storage.local",
    "manifest_version": 2,
    "version": "0.1",
    "permissions": ["activeTab","storage"],
    "content_scripts": [
        {
            "matches": ["<all_urls>"],
            "js": ["content.js"]
        }
    ],

    "background": {
        "scripts": ["background.js"]
    },

    "browser_action": {
        "default_title": "Data not yet available",
        "browser_style": true
    },

    "applications": {
        "gecko": {
        "id": "demo-runtime.connect-and-storage.local@example.com",
        "strict_min_version": "48.0a1"
        }
    }
}

background.js:

//* For testing, open the Browser Console
try{
    //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.');
}
//*
console.log('background: In background script.');

var msgPort;
var dataFromStorage;

// Fires when message port connects to this background script
function connected(prt) {
    msgPort = prt;
    msgPort.onMessage.addListener(gotMessage); //This should be done first
    let message = {type: "message channel established"};
    console.log('background: Port connected: sending confirmation message:', message);
    msgPort.postMessage(message);
}

//Fires when content script sends a message
//Syntax error this line (misspelled function)
function gotMessage(msg) {
    console.log('background: Received message:', msg);
    // store the message
    chrome.storage.local.set({message : msg.type}, function(){
        // read the stored message back again
        chrome.storage.local.get("message", function(item){
            console.log('background: Got data from storage.local.get:',item);
            //You were setting tmp (now dataFromStorage) to the item object, not the
            //  message key which you requested.
            /*
            for(x in item){
                console.log('background: property of item:',x);
            }
            //*/
            dataFromStorage = item.message;
            //Indicate to the user that the data is available
            chrome.browserAction.setTitle({title:'Send data to content script'});
        });
    });
}

// send the saved message to the content script when the add-on button is clicked
chrome.browserAction.onClicked.addListener(function() {
    //msgPort not defined unless user has clicked on page
    if(msgPort) {
        let message = {type: "saved message",data:dataFromStorage};
        console.log('background: Button clicked sending message:', message);
        msgPort.postMessage(message);
    } else {
        console.log('background: No message port available (yet).');
    }
});

//Be open to establishing a connection.  This must be done prior to the
//  chrome.runtime.connect elsewhere in your code. 
chrome.runtime.onConnect.addListener(connected);
console.log('background: Listening for a connection');

content.js:

console.log('\tcontent: Content script injected.');

var isConnected=false;
var retryConnectionTimerId=-1; //In case we want to cancel it
var retryConnectionCount=0;
var messageBeingRetried=null;

//setup message channel between this script and background script
var msgPort;

function messageListener(msg){
    //Using a closure for this function is a bad idea.  This should be a named
    //  function defined at the global scope so we can remove it as a
    //  listener if the background script sends a message to disconnect.
    //  You need to be able to disable any active content scripts if the 
    //  add-on is disabled/removed.  This is a policy from Mozilla. However,
    //  for WebExtensions it is not yet possible due to the current lack of the 
    //  runtime.onSuspend event.
    if(typeof msg === 'object' && msg.hasOwnProperty('type')){
        //Should look specifically for the message indicating connection.
        console.log('\tcontent: Received message: type:', msg.type
            ,(msg.hasOwnProperty('data') ? '\n\t\t\t  Message: data:':'')
            ,(msg.hasOwnProperty('data') ? msg.data : '')
        );
        if(msg.type === 'disableAddon'){
            //Allow for the background script to disable the add-on.
            disableThisScript('Received disableAddon message');
        }
        if(isConnected && msg.type === 'message channel established'){
            //We are in a content script that is left over from a previous load
            //  of this add-on. Or, at least that is the most likely thing 
            //  while testing.  This probably needs to change for a real add-on.
            //  This is here because reloading the temporary add-on does not
            //  auto-disable any content scripts.
            disableThisScript('Received second channel established message');
            return;
        }//else
        isConnected=true; //Any correctly formatted message received indicates connection
        //Immediately send a message that was pending (being retried).
        //  Note: This only immediately sends the message which was most recently attempted
        //  to send via sendMessage, not all messages which might be waiting in timers.
        //  Any others will be sent when their timers expire.
        sendPendingMessageIfPending();
    }else{
        console.log('\tcontent: Received message without a "type":', msg);
    }
}

function receiveDisconnect(){
    //The port was disconnected
    disableThisScript('Port disconnected');
    isConnected=false;
}

function makePortAvailableForConnection(){
    console.log('\tcontent: Making message port available for connection');
    if(msgPort && typeof msgPort.disconnect === 'function'){
        //Call disconnect(), if we have already tried to have a connection
        msgPort.disconnect();
    }
    //Try to make a connection. Only works if ocConnect listener
    //  is already established.
    msgPort = chrome.runtime.connect({name:"msgPort"});
    //Fires when background script sends a message
    msgPort.onMessage.addListener(messageListener);
    msgPort.onDisconnect.addListener(receiveDisconnect);
    //Can not use runtime.onConnect to detect that we are connected.
    //  It only fires if some other script is trying to connect
    //  (using chrome.runtime.connect or chrome.tabs.connect)
    //  to this script (or generally). It does not fire when the connection
    //  is initiated by this script.
    chrome.runtime.onConnect.addListener(portConnected); //Does not fire
}

function portConnected(){
    //This event does not fire when the connection is initiated,
    //  chrome.runtime.connect() from this script.
    //  It is left in this code as an example and to demonstrate that the event does
    //  not fire.
    console.log('\tcontent: Received onConnect event');
    isConnected=true;
}

// sends a message to background script when page is clicked
function sendClickMessage() {
    console.log('\tcontent: Page clicked');
    sendMessage({type: "page clicked"});
            chrome.storage.local.get("message", function(item){
            console.log('content: Got data from storage.local.get:',item);
            });

}

function clearPendingMessage(){
    window.clearTimeout(retryConnectionTimerId);
    messageBeingRetried=null;
}

function sendPendingMessageIfPending() {
    //Pending messages should really be implemented as a queue with each message
    //  being retried X times and then sent once a connection is made. Right now
    //  this works for a single message.  Any other messages which were pending
    //  are only pending for the retry period and then they are forgotten.
    if(messageBeingRetried !== null && retryConnectionTimerId){
        let message = messageBeingRetried;
        clearPendingMessage();
        console.log('\tcontent: Going to send pending message', message);
        sendMessage(message);
    }
}

function retryingMessage(message) {
    retryConnectionTimerId=-1;
    messageBeingRetried=null;
    sendMessage(message);
}

function sendMessage(message) {
    if(isConnected){
        try{
            console.log('\tcontent: Sending message', message);
            msgPort.postMessage(message);
            retryConnectionCount=0;
        }catch(e){
            if(e.message.indexOf('disconnected port') > -1){
                console.log('\tcontent: Sending message failed: disconnected port');
                if(isConnected){
                    console.log('\tcontent: Had connection, but lost it.'
                                + ' Likely add-on reloaded. So, disable.');
                    disableThisScript('Add-on likely reloaded.');
                }else{
                    retryConnection(message);
                }
            }else{
                console.log('\tcontent: Sending message failed: Unknown error', e);
            }
        }
    }else{
        console.log('\tcontent: Sending message failed: not yet connected');
        retryConnection(message);
    }
}

function retryConnection(message){
    if(retryConnectionCount>=5){
        //Limit the number of times we retry the connection.
        //  If the connection is not made by now, it probably won't be
        //  made at all.  Don't fill up the console with a lot of
        //  messages that might make it harder to see what is happening.
        retryConnectionCount=0; //Allow more retries upon another click event.
        //The current message is forgotten. It is now just discarded.
        return;
    }
    console.log('\tcontent: Retrying connection for message:', message);
    makePortAvailableForConnection();
    //Try sending the message after a timeout.
    //  This will result in the repeated attempts to
    //  connect and send the message.
    messageBeingRetried=message;
    retryConnectionTimerId = window.setTimeout(retryingMessage,500,message);
    retryConnectionCount++;
}

function disableThisScript(reason){
    console.log('\tcontent: Disable the content script:', reason);
    //Gracefully disable everything previously set up.
    msgPort.onMessage.removeListener(messageListener);
    msgPort.onDisconnect.removeListener(receiveDisconnect);
    try{
        msgPort.disconnect();
    }catch(e){
        //most likely the port was already disconnected
    }
    chrome.runtime.onConnect.removeListener(portConnected);
    document.body.removeEventListener("click", sendClickMessage);
    isConnected=false;
}

//Making the connection available will silently fail if there is not already a
//  onConnect listener in the background script.  In the case of a "temporary"
//  add-on upon load or reload, the content script is run first and
//  no connection is made.
makePortAvailableForConnection();

document.body.addEventListener("click", sendClickMessage);

NOTE:
This works for a demo, or learning, but won't work well for use in a production extension. There is nothing in the code that accounts for having more than one tab.

You are sending messages to a content script based on clicking on the browser_action button. This means that you need to be able to explicitly send the message from the background script only to the content script that is in the tab being displayed in the active window at the time the user clicks the browser_action button. For example, the user might be in one page, click on the content, then switch to another tab, and click on the browser_action button. In this case, the message should be sent to the currently active tab, not the one which was switched away from (which made the connection).

While you could track the tab which was active when you received the connection or click message (which would work because the connection is actually made, or a message sent, based on a click event (which is assumed to be user input)), it might be better to use tabs.connect() which allows you to establish a connection only with a specific tab. Knowing which runtime.Port corresponds to each tab will allow you to be sure you are sending messages only to the tab which was active at the time the browser_action button was clicked. You would need to keep an array, or object, that contained the connected ports indexed by tab ID.

这篇关于如何在Firefox WebExtensions加载项中使用chrome.storage和runtime.connect的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-23 18:45