Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to efficiently store/retrieve data to/from chrome.storage.sync?

So, I'm writing an extension to allow people to fine and save colors from images found on the web. It's going well but now I'm trying to conceptualize how I'll actually store them, and list stored items.

As far as I can tell, chrome.storage.sync() only allows for objects. Which means I'd have to do something like this:

{colors: [{colorName: 'white', colorHex: '#ffffff'}, {colorName: 'black', colorHex: '#000000'}]}

Which seems wildly inefficient, since every time I want to add or subtract a color from the favorite list, I will need to get the entire array, change the one item I want, and then store the array back. Not to mention scanning an array for a color to see if it exists or not could be very intensive on a large array.

Ultimately, I'd like to be able to do something along the lines of

 colors['#fff'].name = white;

However, that doesn't seem possible.

I'd love to hear some other ideas as to what the best way to accomplish this might be.

like image 427
Chris Sobolewski Avatar asked Jun 19 '13 20:06

Chris Sobolewski


2 Answers

I was unsure about this as well, so I made a small example.

manifest.json:

{
    "manifest_version": 2,

    "name": "Test",
    "description": "Test.",
    "version": "1.0",

    "permissions": [
        "storage"
    ],
    "content_scripts": [
        {
            "matches": ["https://www.google.com/*"],
            "js": ["content-script.js"]
        }
    ]
}

content-script.js:

console.log("content script loaded")

function modifyObject() {
    chrome.storage.sync.get(null, function(storageData3) {
        storageData3.object.property2 = false;

        chrome.storage.sync.set(storageData3, function() {
            chrome.storage.sync.get(null, function(storageData4) {
                console.log("after setting *only* object: " + JSON.stringify(storageData4));
            });
        });
    });
}

// Dumb attempt at setting only property2 of "object"; will add a new top level object "property2".
function attemptToModifyProperty2() {
    var toSave = { "property2": false };
    chrome.storage.sync.set(toSave, function() {
        chrome.storage.sync.get(null, function(storageData2) {
            console.log("after attemping to set *only* property2: " + JSON.stringify(storageData2));

            modifyObject();
        });
    });
}

function addArray() {
    var toSave = { "array": [1, 2, 3] };
    chrome.storage.sync.set(toSave, function() {
        chrome.storage.sync.get(null, function(storageData1) {
            console.log("after setting *only* array: " + JSON.stringify(storageData1));

            attemptToModifyProperty2();
        });
    });
}

function addObject() {
    var toSave = { "object": { "property1": true, "property2": true } };
    chrome.storage.sync.set(toSave, function() {
        chrome.storage.sync.get(null, function(storageData) {
            console.log("after setting *only* object: " + JSON.stringify(storageData));

            addArray();
        });
    });
}

chrome.storage.sync.clear();

addObject();

If you go to google.com (and log in, or change the matches in manifest.json to http), and then open the console, you'll see this output:

content script loaded
content-script.js:42 after setting *only* object: {"object":{"property1":true,"property2":true}}
content-script.js:31 after setting *only* array: {"array":[1,2,3],"object":{"property1":true,"property2":true}}
content-script.js:20 after attemping to set *only* property2: {"array":[1,2,3],"object":{"property1":true,"property2":true},"property2":false}
content-script.js:9 after setting *only* object: {"array":[1,2,3],"object":{"property1":true,"property2":false},"property2":false}

My conclusions from this were that it's only possible to set top-level objects. Even if you want to change only one property that is nested deeply within a top-level object, you will have to pass the entire object to set().

like image 33
Mitch Avatar answered Oct 14 '22 17:10

Mitch


The beauty of Javascript is that everything is loosely considered an object. Functions, arrays, and even variables can be accessed as objects.

You could create an array like this,

var colors {}
colors["#FFF"] = "white";
colors["#000"] = "black";

Or perhaps use an array of empty functions,

function color(name, hex /* ... other properties */ ) { }

var colors {
    color1: color("white", "#FFF");
    color2: color("black", "#000");
}

Then these colors can be accessed by

color1.name

or

color1.hex

Although, because you should use a specific 'key' value for each object in storage, perhaps that is a better way to go.

For instance,

function save_color() {
    var white = "#FFF";
                             //key    value   callback
    chrome.storage.sync.set({"white": white}, function() {
        console.log("The value stored was: " + white);
    });
}

Or, for multiple colors

function save_colors() {
    var white = "#FFF";
    var black = "#000";

    chrome.storage.sync.set([{"white": white}, {"black": black}], function() {
        console.log("The values stored are: " + white + " and " + black);
    });
}

I think that may work, i haven't tried storing multiple objects using one api call before, but you should get the point. A good way to implement this may be to have an empty array that gets added to every time the user finds a color they would like to add, then periodically the extension can push the data to sync.

Once you have done a ton of testing and your sync storage is cluttered, keep track of the keys you used during development and remember to run a batch data removal. It would look something like this:

function clear_data() {
    var keys = { "white", "black" };
    chrome.storage.sync.remove(keys, function() {
        for(var i = 0; i < keys.length; i++)
            console.log("Removed Data for Key: " + key[i]);
    });
}

By the way, to retrieve the value stored in sync,

function load_color() {
    var color = "white";
                           //key   callback
    chrome.storage.sync.get(color, function(val) {
        console.log("The value returned was: " + val);
    });
}
like image 177
David Freitag Avatar answered Oct 14 '22 17:10

David Freitag