Search code examples
javascriptgoogle-chrome-extensionchrome-extension-manifest-v3

How to implement localStorage logic with chrome.storage.local (avoid async logic)


For a Manifest 3 browser extension I need to implement local storage logic. This is because there is a lot of code that are shared between different projects that uses localStorage. To change and test all this code on different platforms will be quite a big job.

So I am trying to make a proxy of an object that implement the normal functions of the localStorage object. In the extension I need to use async functions like chrome.storage.local.get.

This gives me a lot of problems with the Promise logic in different ways as getItem return a promise (not intended) or I get runtime errors like "await is only valid in async functions and the top level bodies of modules" etc.

The code below is one such try:

var localStorageTarget = {};

localStorageTarget.getItem = async function(keyname)
{
  const internalGetItem = async () =>
  {
    return new Promise((resolve, reject) => 
    {
      chrome.storage.local.get([keyname], function (result) 
      {
        if (result[keyname] === undefined) 
        {
          reject();
        } 
        else 
        {
          resolve(result[keyname]);
        }
      });
    });
  }

  return await internalGetItem();
};

localStorageTarget.setItem = function(keyname, keyvalue)
{
    chrome.storage.local.set({keyname: keyvalue});
    return true;    
};

localStorageTarget.removeItem = function(keyname)
{
    chrome.storage.local.remove([keyname]);
    return true; // deleteProperty need this    
};

const localStorage = new Proxy(localStorageTarget, 
{
  get: function(obj, name) 
  {
      //console.log('Read request to ' + name + ' property with ' + obj[name] + ' value');
      
      return obj.getItem(name);
  },
  set: function(obj, name, value) 
  {
      //console.log('Write request to ' + name + ' property with ' + value + ' value');
      
      return obj.setItem(name, value);
  },
  deleteProperty(obj, name) 
  {
    //console.log('Delete request to ' + name + ' property with ' + obj[name] + ' value');
    
    return obj.removeItem(name);
  } 
});

localStorage['qqqq'] = 'test2';
console.log(localStorage.getItem('qqqq'));
console.log(localStorage['qqqq']);
delete localStorage['qqqq'];

In advance thank you for for any hint or help

/Benny


Solution

  • You're not going to get around the fact that if the getter returns a Promise, you have to await that promise at the top level:

    console.log(await localStorage['qqqq']);
    

    Since chrome.storage.local.get() returns a promise if you don't pass a callback, you can simplify your code:

    localStorageTarget.getItem = function(keyname) {
      return chrome.storage.local.get([ keyname ]);
    }
    

    And lastly, I think that you want expand keyname into a property name:

    chrome.storage.local.set({ [ keyname ]: keyvalue });