通过 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:

  1. 从回调中使用 return true 以允许异步发送响应
  2. 在 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 不同消息线程中的数据:

  1. SW.readTable() 的 service worker 中,只需 return 变量 oo.response = true,然后忽略内容脚本中的响应。

  2. 在 return 从 SW.readTable() 调用变量 o 之前,执行 chrome.runtime.sendMessage({readTableResult = true, data: asEntries},function(response){ /* ignore response */});

  3. 在内容脚本中,忽略 readTable 消息返回的任何响应。所以,if (response.response) {...}条件可以排除

  4. 在内容脚本中,添加带有chrome.runtime.onMessage.addListener(o, sender, sendResponse)的消息侦听器并查找条件(o.readTableResult)。收到后,o.data 现在将包含 asEntries 数据。