通过 Chrome Runtime Messaging 从 Content Script 访问 Service Worker 保存的 IndexedDB 数据
Accessing Service Worker saved IndexedDB data from Content Script via Chrome Runtime Messaging
在 Chrome 扩展中,我可以毫无问题地添加、更新和删除数据 to/from 我的服务工作者使用 Chrome 从我的内容发送的运行时消息访问的 IndexedDB 数据库脚本。我的麻烦是从我的内容脚本中进行完整的 table 读取。在 Chrome Runtime Messaging 的 sendResponse 中将它发回之前,我执行了 console.log() 以转储出 属性,我在那里正确地看到了数据,但是内容脚本收到了一个 undefined
。我认为这是因为获取数据的异步性质。我尝试了 promises 和 async/await 以及它们的组合,但我似乎无法在服务工作者返回的消息的内容脚本中得到 undefined
之外的任何东西。我还 运行 发回了一个测试数组并且工作得很好——但是接收 IndexedDB table 数据在消息传递中不起作用。我还尝试对数据进行 JSONify,但这也无济于事。有什么收获?
服务-worker.js
importScripts('modules/idb.js');
var SW = {};
SW.onReady = function(){
chrome.runtime.onMessage.addListener(function(o, sender, sendResponse) {
(o.readTable) && sendResponse(SW.readTable(o,sender));
});
};
SW.readTable = function(o,sender){
var sTable = o.table;
new Promise((resolve) => {
IDB.readTable(sTable,function(asEntries){
resolve(asEntries);
});
}).then((asEntries) => {
console.log('SW asEntries',asEntries); // this shows me valid data in tests
var o = {};
// can also change this to fake data with asEntries being a string array and bug goes away in content.js
o.response = asEntries;
return o;
});
};
SW.onReady();
modules/idb.js
var IDB = {};
// Requires storage (or, even better, unlimitedStorage) permission in your manifest.json file.
// Note also that dev console of service worker will not show data -- have to use toolbar button popup panel (if you have one) and
// dev console from there, or code to access it, which sucks.
IDB.connectStore = function(sTable,sReadWriteSetting,fn){
var conn = indexedDB.open('unlimitedStorage', 1);
conn.onupgradeneeded = function(e) {
var db = e.target.result;
db.createObjectStore(sTable);
};
conn.onsuccess = function(e) {
var db = e.target.result;
var tx = db.transaction(sTable,sReadWriteSetting);
var store = tx.objectStore(sTable);
fn(db,tx,store);
};
};
IDB.addToTable = function(sTable,sKey,sVal){
IDB.connectStore(sTable,'readwrite',function(db,tx,store){
if ((sKey === undefined) || (sKey === '') || (sKey === null) || (sKey === false)) { // auto key by increment
var req = store.count();
req.onsuccess = function(e){
sKey = e.target.result + 1;
store.add(sVal,sKey);
tx.complete;
}
} else {
store.add(sVal,sKey);
tx.complete;
}
});
};
IDB.removeFromTable = function(sTable,sKey){
IDB.connectStore(sTable,'readwrite',function(db,tx,store){
store.delete(sKey);
tx.complete;
});
};
IDB.readTableByKey = function(sTable,sKey,fn){
IDB.connectStore(sTable,'readonly',function(db,tx,store){
var req = store.get(sKey);
req.onerror = function(e){
fn(e.target.result);
}
req.onsuccess = function(e){
fn(e.target.result);
}
});
};
IDB.readTable = function(sTable,fn){
IDB.connectStore(sTable,'readonly',function(db,tx,store){
var req = store.getAll();
req.onerror = function(e){
fn(e.target.result);
}
req.onsuccess = function(e){
fn(e.target.result);
}
});
};
content.js
var CONTENT = {};
CONTENT.onReady = function(){
var o = {};
o.readTable = true;
o.table = 'loadTimes';
chrome.runtime.sendMessage(o,function(response){
if (response.response) { // errors here with response property being undefined
console.log('CONTENT RCVD asEntries',response.response);
}
});
};
CONTENT.onReady();
Chrome 扩展 API,与 Firefox WebExtensions 不同,无法处理从回调返回或在 sendResponse 中提供的 Promise,https://crbug.com/1185241.
你的 readTable 也有一个错误:你需要在 new Promise((resolve)
之前添加 return
解决方案是two-fold:
- 从回调中使用
return true
以允许异步发送响应
- 在 Promise 链的
.then
内调用 sendReponse
。
chrome.runtime.onMessage.addListener(function(o, sender, sendResponse) {
if (o.readTable) {
SW.readTable(o,sender).then(sendResponse);
return true;
} else {
sendResponse(); // Chrome 99-101 bug workaround, https://crbug.com/1304272
}
});
不要使用这个答案。它在这里是为了后代的原因,只是一种解决方法。所选解决方案有效。
解决方法是 return 不同消息线程中的数据:
在 SW.readTable()
的 service worker 中,只需 return 变量 o
和 o.response = true
,然后忽略内容脚本中的响应。
在 return 从 SW.readTable()
调用变量 o
之前,执行 chrome.runtime.sendMessage({readTableResult = true, data: asEntries},function(response){ /* ignore response */});
在内容脚本中,忽略 readTable
消息返回的任何响应。所以,if (response.response) {...}
条件可以排除
在内容脚本中,添加带有chrome.runtime.onMessage.addListener(o, sender, sendResponse)
的消息侦听器并查找条件(o.readTableResult)
。收到后,o.data
现在将包含 asEntries
数据。
在 Chrome 扩展中,我可以毫无问题地添加、更新和删除数据 to/from 我的服务工作者使用 Chrome 从我的内容发送的运行时消息访问的 IndexedDB 数据库脚本。我的麻烦是从我的内容脚本中进行完整的 table 读取。在 Chrome Runtime Messaging 的 sendResponse 中将它发回之前,我执行了 console.log() 以转储出 属性,我在那里正确地看到了数据,但是内容脚本收到了一个 undefined
。我认为这是因为获取数据的异步性质。我尝试了 promises 和 async/await 以及它们的组合,但我似乎无法在服务工作者返回的消息的内容脚本中得到 undefined
之外的任何东西。我还 运行 发回了一个测试数组并且工作得很好——但是接收 IndexedDB table 数据在消息传递中不起作用。我还尝试对数据进行 JSONify,但这也无济于事。有什么收获?
服务-worker.js
importScripts('modules/idb.js');
var SW = {};
SW.onReady = function(){
chrome.runtime.onMessage.addListener(function(o, sender, sendResponse) {
(o.readTable) && sendResponse(SW.readTable(o,sender));
});
};
SW.readTable = function(o,sender){
var sTable = o.table;
new Promise((resolve) => {
IDB.readTable(sTable,function(asEntries){
resolve(asEntries);
});
}).then((asEntries) => {
console.log('SW asEntries',asEntries); // this shows me valid data in tests
var o = {};
// can also change this to fake data with asEntries being a string array and bug goes away in content.js
o.response = asEntries;
return o;
});
};
SW.onReady();
modules/idb.js
var IDB = {};
// Requires storage (or, even better, unlimitedStorage) permission in your manifest.json file.
// Note also that dev console of service worker will not show data -- have to use toolbar button popup panel (if you have one) and
// dev console from there, or code to access it, which sucks.
IDB.connectStore = function(sTable,sReadWriteSetting,fn){
var conn = indexedDB.open('unlimitedStorage', 1);
conn.onupgradeneeded = function(e) {
var db = e.target.result;
db.createObjectStore(sTable);
};
conn.onsuccess = function(e) {
var db = e.target.result;
var tx = db.transaction(sTable,sReadWriteSetting);
var store = tx.objectStore(sTable);
fn(db,tx,store);
};
};
IDB.addToTable = function(sTable,sKey,sVal){
IDB.connectStore(sTable,'readwrite',function(db,tx,store){
if ((sKey === undefined) || (sKey === '') || (sKey === null) || (sKey === false)) { // auto key by increment
var req = store.count();
req.onsuccess = function(e){
sKey = e.target.result + 1;
store.add(sVal,sKey);
tx.complete;
}
} else {
store.add(sVal,sKey);
tx.complete;
}
});
};
IDB.removeFromTable = function(sTable,sKey){
IDB.connectStore(sTable,'readwrite',function(db,tx,store){
store.delete(sKey);
tx.complete;
});
};
IDB.readTableByKey = function(sTable,sKey,fn){
IDB.connectStore(sTable,'readonly',function(db,tx,store){
var req = store.get(sKey);
req.onerror = function(e){
fn(e.target.result);
}
req.onsuccess = function(e){
fn(e.target.result);
}
});
};
IDB.readTable = function(sTable,fn){
IDB.connectStore(sTable,'readonly',function(db,tx,store){
var req = store.getAll();
req.onerror = function(e){
fn(e.target.result);
}
req.onsuccess = function(e){
fn(e.target.result);
}
});
};
content.js
var CONTENT = {};
CONTENT.onReady = function(){
var o = {};
o.readTable = true;
o.table = 'loadTimes';
chrome.runtime.sendMessage(o,function(response){
if (response.response) { // errors here with response property being undefined
console.log('CONTENT RCVD asEntries',response.response);
}
});
};
CONTENT.onReady();
Chrome 扩展 API,与 Firefox WebExtensions 不同,无法处理从回调返回或在 sendResponse 中提供的 Promise,https://crbug.com/1185241.
你的 readTable 也有一个错误:你需要在 new Promise((resolve)
return
解决方案是two-fold:
- 从回调中使用
return true
以允许异步发送响应 - 在 Promise 链的
.then
内调用sendReponse
。
chrome.runtime.onMessage.addListener(function(o, sender, sendResponse) {
if (o.readTable) {
SW.readTable(o,sender).then(sendResponse);
return true;
} else {
sendResponse(); // Chrome 99-101 bug workaround, https://crbug.com/1304272
}
});
不要使用这个答案。它在这里是为了后代的原因,只是一种解决方法。所选解决方案有效。
解决方法是 return 不同消息线程中的数据:
在
SW.readTable()
的 service worker 中,只需 return 变量o
和o.response = true
,然后忽略内容脚本中的响应。在 return 从
SW.readTable()
调用变量o
之前,执行chrome.runtime.sendMessage({readTableResult = true, data: asEntries},function(response){ /* ignore response */});
在内容脚本中,忽略
readTable
消息返回的任何响应。所以,if (response.response) {...}
条件可以排除在内容脚本中,添加带有
chrome.runtime.onMessage.addListener(o, sender, sendResponse)
的消息侦听器并查找条件(o.readTableResult)
。收到后,o.data
现在将包含asEntries
数据。