chrome.storage.sync vs chrome.storage.local - google-chrome

I was trying to understand how to use the chrome.storage.api.
I have included the following in my manifest.json:
"permissions": [
"activeTab","storage"
],
Than, I opened a new tab with the devtools and switched the <page context> to the one of my chrome-extension. Than I typed:
chrome.storage.sync.set({"foo":"bar"},function(){ console.log("saved ok"); } );
and got:
undefined
saved ok
Than I tried getting this stored value:
chrome.storage.sync.get("foo",function(data){ console.log(data); } );
but this got me:
undefined
Object {}
Than I did the same, but instead of sync I used local and this worked as expected:
chrome.storage.local.set({"foo":"bar"},function(){ console.log("saved ok"); } );
..and the retrieval:
chrome.storage.local.get("foo",function(data){ console.log(data); } );
Which got me: Object {foo: "bar"} as it should.
Is this because I am not signed in to my account on chrome? But in that case, isn't chrome.storage.sync designed to fallback into storing the data locally?
EDIT
Strangely, when i type this straight on console it seems to be working, but this code doesn't run from background.js code inside a click listener:
var dataCache = {};
function addStarredPost(post)
{
var id = getPostId(post);
var timeStamp = new Date().getTime();
var user = getUserName();
dataCache[id] = {"id":id,"post":post,"time":timeStamp,"user":user};
chrome.storage.sync.set(dataCache,function(){ console.log("Starred!");});
}
After this is ran, chrome.storage.sync.get(null,function(data){ console.log(data); }); returns an empty object as if the data wasn't stored. :/
This code seems to be working perfect with chrome.storage.local instead.
chrome.runtime.lastErros returns undefined

The max size for chrome local storage is 5,242,880 bytes.
To extend the storage you can add on the manifest.json :
"permissions": [
"unlimitedStorage"
]
The max size for chrome sync storage is:
102,400 bytes total
8,192 bytes per item
512 items max
1,800 write operations per hour
120 operations per minutes
(source)

Whoops!
The problem was I was trying to sync data that exceeded in size. (4096 Bytes per item)
I wasn't getting chrome.runtime.lastError because I was mistakenly putting it inside the get function scope, instead of the set function which was producing the error. Hence, I'm posting this answer so it might help others who share the same confusion.
You should check chrome.runtime.lastError inside each api call, like so:
chrome.storage.local.set(objectToStore, function(data)
{
if(chrome.runtime.lastError)
{
/* error */
console.log(chrome.runtime.lastError.message);
return;
}
//all good. do your thing..
}
This ran OK with chrome.storage.local because according to the docs you only have this limitation with sync.
printing chrome.runtime.lastError gave me: Object {message: "QUOTA_BYTES_PER_ITEM quota exceeded"}

Related

IndexedDB flush to disk on Chrome

I'm facing an issue with IndexedDB on Chrome where I reload my page once the transaction returns a successful write.
Problem is sometimes that data does not reflect after reload. I can solve this by giving a timeout of about 100ms before reload, which leads me to believe that the data is not flushed to disk everytime.
Firexox has an experimental readwriteflush mode which ensures data is flushed to disk before returning a success call, but can't seem to find a similar one for Chrome. Any suggestions?
Here's my insert code:
const data = {type: type, value: value};
const objectStore = StorageService.db.transaction(['localData'], 'readwrite').objectStore('localData');
// readwriteflush doesn't work in chrome
// const objectStore = StorageService.db.transaction(['localData'], 'readwriteflush').objectStore('localData');
const requestSet = objectStore.put(data);
requestSet.onerror = function (event) {
alert('Error in saving data locally');
};
requestSet.onsuccess = function (event) {
console.log('Data was successfully saved locally: ' + type);
if (callback != undefined) {
callback();
}
};
The callback has location.reload = '/'; executed in it (along with some other things), so the page reloads after the onsuccess has been returned.
After the page reloads, I cannot see any data on my IndexedDB storage, both via code and on developer tools. This does not always happen however, I've observed that this happens only when data is larger than usual.
"success" fired at a request does not indicate that the transaction has committed successfully. The transaction could later fail due to a separate failed request (e.g. a conflicting add call), I/O error, or e.g. power loss.
You need to wait for the "complete" event to be fired at the transaction. Chrome flushes to disk before firing the "complete" event.

Chrome doesn't use cache after power loss?

I am creating a digital signage player that uses Chrome as it's display engine. We need to be able to still muddle along if the network goes down without too much interruption.
Chrome works fine caching images, and I've set the "Exipres" header to be a month after access. I can set the player computer offline and have the app run for days with no problem. If I reboot the machine the right way (Start->Shut Down), caching still works as expected.
The issue is that when Chrome exits abnormally - Either a crash or power loss - on reboot, Chrome ignores the cache and refuses to load images. This happens if I cut power 5 minutes after it loads the page, so content is not expiring.
My guess is that Chrome is set to ignore the cache after an abnormal exit to prevent corrupted cache from continually crashing the browser. However, this behavior is not what I need.
Does anyone know of a command line arg or flag I can set to keep this from happening?
Thanks for your help.
I tried everything I could think of to make Chrome not invalidate the local cache on system failure, and came up empty. There's a few other people who had the same question, and I didn't see an answer.
Here's what I did that made this work, and if someone else is having the same problem, it might be the workaround that you need.
I added a service worker that would cache images. The code below isn't perfect yet, but should be a starting place for someone... (FYI, I learned this 5 minutes ago, so if someone wants to give me a pointer or two on how to make this more elegant, I'm all ears.)
We cache anything that has a response type of "cors" so we cache only images coming from the remote server. Note that your images must be loaded via https for this to work.
Taken (mostly) from: https://developers.google.com/web/fundamentals/getting-started/primers/service-workers
var CACHE_NAME = 'shine_cache';
var urlsToCache = [
'/'
];
self.addEventListener('install', function(event) {
// Perform install steps
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
self.addEventListener('fetch', function(event) {
//console.log('Handling fetch event for', event.request);
if (event.request.method == 'POST') {
//console.log("Skipping POST");
event.respondWith(fetch(event.request));
return;
}
if (event.request.headers.get('Accept').indexOf('image') !== -1) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
// Cache hit - return response
if (response) {
console.log("Returning from cache.", event.request);
return response;
}
// IMPORTANT: Clone the request. A request is a stream and
// can only be consumed once. Since we are consuming this
// once by cache and once by the browser for fetch, we need
// to clone the response.
var fetchRequest = event.request.clone();
return fetch(fetchRequest).then(
function(response) {
console.log("Have a response.", response);
// Check if we received a valid response
if(!response || response.status !== 200 || response.type !== 'cors') {
return response;
}
// IMPORTANT: Clone the response. A response is a stream
// and because we want the browser to consume the response
// as well as the cache consuming the response, we need
// to clone it so we have two streams.
var responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(function(cache) {
console.log("Caching response", event.request);
cache.put(event.request, responseToCache);
});
return response;
}
);
})
);
}
});

Only fetch objects with specified keys in Firebase from large index

I have an array = [ 'something', 'other' ]
And I want to retrieve only the values of those 2 ids from Firebase, which contains more than 2 items ( potentially millions ), but if I do this:
var questionRef = new Firebase(fireBaseURL+"/morethanamillionitems/");
loadUID.once('value', function (dataSnapshot) {
dataSnapshot.forEach(function(childSnapshot) { // Firebase method
console.log(dataSnapshot.numChildren()); // potentially outputs 1.000.000 +
var uid = childSnapshot.name();
var childData = childSnapshot.val();
console.log(uid.indexOf('something'));
result.push(uid)
});
}
I first basically load the whole database, which is not that efficient
Now I could do:
array.forEach(key, function() {
var questionRef = new Firebase(fireBaseURL+"/morethanamillionitems/"+key);
refID = questionRef.val();
result.push(refID);
})
Or maybe:
questionRef = new Firebase(fireBaseURL+"/morethanamillionitems/");
array.forEach(key, function() {
if ( questionRef.child(key) !== null ){
refID = questionRef.val();
result.push(refID);
}
})
The last one seems the nicest, the previous one seems a bit expensive on the old RAM.
However, I apparently have to call questionRef.once('value', function(){}) each time, hence already loading the whole document-root...
Or am I misunderstanding how Firebase handles these requests? is the .numChildren() just an answer directly from the server?
Is the .forEach actually remotely executed?
I'm wondering if there is any other way to reduce traffic per request. Which brings me to another question: it seems that firebase searches locally first, but eventually will search remotely, but it's not clear when this exactly happens. Does it periodically check if something has changed? Or will that only happend when I use .on() and not .once().
Or am I using the wrong backend for this purpose? Any other suggestions? I tried hood.ie which is still very beta, looked at Parse but firebase seemed to have the simplicity I need.
(sorry for the sloppy syntax, but you can see what I intended)
[update]
I now have this:
load: function(uids){
var FB = new Firebase(URL);
uids.map(function(uid) {
var currentRef = FB.child( uid+"/_current" );
currentRef.once('value', function (each) {
eachVal = each.val()
if (eachVal !== null){
var localSave = {};
localSave[uid] = eachVal;
this.saveLocal(localSave)
} else {
console.error("Not found: [%s]", uid)
}}, function (err) { });
});
}
But I'm still wondering when the request actually happens, on .child()? or in .once() and if the latter, what is the use of .child() exactly? It seems it's only used for referencing.
Then the second thing, if I want to retrieve an array of a hundred items, this would still mean a hundred seperate requests? or does Firebase have a way of collecting requests and then send them in a batch?
In that last case .once would be a more 'conservative' option for initial retrieval, then later you could attach a .on listener if you need real-time updates.

WebSQL changeVersion always fail

I'm trying to create a bookmark extension in Chrome and I want to leverage WebSQL to store all kind of information about bookmarks locally. Here's what I've done so far:
(function() {
var Home,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
Home = (function() {
function Home() {
this.onDBInit = __bind(this.onDBInit, this); this.db = openDatabase("Journal", "", "Bookmarks Stats", 5 * 1024 * 1024, this.onDBInit, this.onDBError);
}
Home.prototype.onDBInit = function(db) {
console.log(db.version);
db.changeVersion("", "1.0", this.initDB, this.onDBError);
return console.log(db);
};
Home.prototype.initDB = function(t) {
console.log(t);
return t.executeSql('CREATE TABLE bookmarks (id, title, url)');
};
Home.prototype.onDBError = function(e) {
return console.log(e);
};
return Home;
})();
window.Registerable(Home);
}).call(this);
For some reason, changeVersion ALWAYS fails. I have tried to delete the database, restart chrome, etc. Chrome version: 18.
In my limited experience, changeVersion does work on Chrome. I've also read complaints here about it not working properly on Safari, but it does.
However, there are two catches:
Catch 1:
Often, changeVersion appears to fail (it gives an error and db.version will still return the old value), but the transaction callback will fire and when you re-open the web page and its database, the version number will be correct.
Catch 2:
It seems you must supply precisely five arguments, including three callbacks. If you don't supply these five arguments, for example you only do the first three, then the current version will remain unchanged. So if you were to follow the instruction in this tutorial that everyone's referring to, you would be disappointed.
This is the case for Chrome and Safari on Windows.
Arguments are:
1: expected version
2: new version
3: transaction callback (you can execute SQL here as part of the version change)
4: failure callback (if version change or transaction callback failed)
5: succes callback (if version change and transaction callback succeeded)
I haven’t tested this on iOS devices but it matches the Safari specifications provided here:
https://developer.apple.com/library/safari/#documentation/iphone/conceptual/safarijsdatabaseguide/usingthejavascriptdatabase/usingthejavascriptdatabase.html
In Opera, changeVersion waits with setting db.version to its new value until after you've executed an actual sql transaction using executeSql. So I use a SELECT statement that has no further consequence. This does not actually have to be inside the transaction callback: I discovered it when I tried a separate executeSql statement in the browser console after trying a whole bunch of changeVersion commands.
This is Chrome bug: you can not change the version of the database with an empty string in the value.
And according to the specifications in the call creationCallback (in your case, the function onDBInit) version of the database is exposed to an empty string:
the callback is invoked with the database having the empty string as its version regardless of the given database version
This error is detected two years ago and described in detail, but is still not corrected
I have decided this issue in the following ways: I removed the initialization of the database structure of creationCallback and made it the first transaction to the database.
var db = openDatabase("Journal", "0.1", "Bookmarks Stats", 5 * 1024 * 1024);
db.transaction(function (t) {
t.executeSql('CREATE TABLE IF NOT EXISTS bookmarks (id, title, url)');
});

Geolocation HTML5 enableHighAccuracy True , False or Best Option?

i have a problem about HTML5 geolocation feature. I use the code below to get location data. I use "enableHighAccuracy: false" option to work with Cell Based GPS feature. Accurancy is low but response it too fast. But some people always use Built-in GPS with their mobile phone, so this code does not work for them. Bu if i change accurency option as "enableHighAccuracy: true" it works for them. But this time, the code uses only built-in GPS. not CELL based GPS.
The question -> How can i do that : First, try to get position from Built-in GPS with timeout (e.g. 5000ms ) if position cannot be got in this time just look for Cell Based position for timeout (e.g. 10000ms) if position cannot be get in this time, return an error message .
Here is the code that i use now.
Thanks in advance.
function getLocationfromGoogle() {
navigator.geolocation.getCurrentPosition(
function(pos) {
$("#lat_field").val(pos.coords.latitude);
$("#long_field").val(pos.coords.longitude);
var geocoder = new google.maps.Geocoder();
var latLng = new google.maps.LatLng(pos.coords.latitude,pos.coords.longitude);
geocoder.geocode({ 'latLng': latLng}, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
//console.log(results[0].formatted_address);
$("#adresim").val(results[0].formatted_address);
}
else {
alert('Google convertion is not succesfully done.');
}
});
},function error(msg){
alert('Please enable your GPS position future.');
},{maximumAge:600000, timeout:5000, enableHighAccuracy: false}
);
}
You should also be aware that the implementation of this varies from phone OS to phone OS - what works on Android may or may not work on iOS, BlackBerry, WindowsPhone, etc.
You're almost there, you just need to:
Specify enableHighAccuracy: true (you have it set to false)
Handle the timeout error case in the error handler. If the error from the high accuracy query is timeout, then try it again with enableHighAccuracy: false.
Have a look at this sample code.
You should also note that when testing this on a few devices, it returns location derived from WiFi even when enableHighAccuracy: true.
The code mentioned here: http://jsfiddle.net/CvSW4/ did not work for me during error handling.
The reason is that the error functions accept a parameter named 'position' but use an object in the functions called 'error'.
function errorCallback_highAccuracy(position) { ... }
function errorCallback_lowAccuracy(position) { ... }
The solution to fix this was to switch the error methods to accept the input value as a parameter named 'error' and not 'position', since the error callbacks do not accept a position and throw an error object instead.
function errorCallback_highAccuracy(error) { ... }
function errorCallback_lowAccuracy(error) { ... }
I mention it here, because I could not post on the resulting example page and also, this is the location where I linked through to find the code sample mentioned above.