Accessing Service Worker saved IndexedDB data from Content Script via Chrome Runtime Messaging - javascript

In a Chrome Extension, I have no problem adding, updating, and removing data to/from an IndexedDB database accessed by my service worker with Chrome Runtime Messaging sent from my content script. My trouble is doing a full table read from my content script. I do a console.log() to dump out the property before I send it back in my sendResponse in the Chrome Runtime Messaging, and I see the data there properly, but the content script receives an undefined. I assume this is because of the asynchronous nature of getting the data. I tried promises and async/await and the combination thereof and I just can't seem to get anything except an undefined in my content script on the message back from the service worker. I also ran sending a test array back and that worked just fine -- but receiving the IndexedDB table data does not work in the message passing. I also tried to JSONify the data and that didn't help either. What's the catch?
service-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 extensions API, unlike Firefox WebExtensions, can't handle Promise returned from a callback or provided in sendResponse, https://crbug.com/1185241.
There's also a bug in your readTable: you need to add return before new Promise((resolve)
The solution is two-fold:
Use return true from the callback to allow asynchronous sendResponse
Call sendReponse inside .then of a Promise chain.
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
}
});

Do not use this answer. It is here for posterity reasons and is just a workaround. The chosen solution works.
The fix is to return data in a different message thread:
In the service worker in SW.readTable(), just return variable o with o.response = true and then ignore the response in the content script.
Before returning the variable o from SW.readTable(), do a chrome.runtime.sendMessage({readTableResult = true, data: asEntries},function(response){ /* ignore response */});
In the content script, ignore any response back from the readTable message. So, the if (response.response) {...} condition can be eliminated.
In the content script, add a message listener with chrome.runtime.onMessage.addListener(o, sender, sendResponse) and look for the condition (o.readTableResult). Once received, the o.data will now contain the asEntries data.

Related

Communication problem between background script and content script

I am trying to build an addon working on both firefox and chrome in order to get started with extensions (it's not an extension to be published). My aim is basically to recover the mouse movements and a screenshot of the tab and send them through a REST API.
Everything was working fine as of yesterday, but as I am testing today, I am getting an error stating that the content script isn't able to communicate with my background script.
I have checked if a new version of firefox was released overnight and it doesn't seem to be the case to my knowledge. I then have extensively checked the google runtime APIs, and my code seems to be constructed accordingly to the documentation.
What's more intriguing is that everything was working fine even with multiple trials.
If I try to print the content of the sent message, it doesn't print anything.
When I load a temporary addon on firefox, I get a temporary ID, and the error I get is the following :
Error: Could not establish connection. Receiving end does not exist.
background.js:1:745 Unchecked lastError value: Error: Could not
establish connection. Receiving end does not exist. background.js:1
sendRequestToSpecificTab
moz-extension://94826cb7-3494-4f28-abda-e0dbb477ca37/js/background.js:1
I noticed that the ID specified in the error is different than the ID that is provided when I load the addon.
Here is my code :
keylog.js
//capturing the mouse movements
window.addEventListener("mousemove", logMouseMove, false);
window.addEventListener("mousedown", logMouseDown, false);
function logMouseMove(e) {
let currentdate = Date.now();
chrome.runtime.sendMessage({action: "mouselog",data : currentdate+': ('+e.pageX+','+e.pageY+')'});
}
function logMouseDown(e) {
let currentdate = Date.now();
chrome.runtime.sendMessage({action: "mouselog",data :currentdate+': ['+e.pageX+','+e.pageY+']'});
}
And background.js :
"use strict";
let concatenated = "";
let mouse_coord={};
let mouse_json = {};
var id = "";
chrome.runtime.onMessage.addListener(msg => {
var ID = function () {
return '_' + Math.random().toString(36).substr(2, 9);
};
if(msg.action == "mouselog") {
let splitted = msg.data.split(':');
mouse_coord[splitted[0]] = splitted[1];
console.log(msg.data);
if(splitted[1].charAt(0) === '[') {
id = ID();
mouse_json[id] = mouse_coord;
mouse_coord = {};
concatenated += JSON.stringify(mouse_json, null, 4);
let donnes= {};
donnes['id'] = id;
donnes['data'] = mouse_json;
chrome.tabs.query({active: true, currentWindow:true}, (tab) => {
donnes['tab'] = tab;
sendData('http://localhost:33333/mouselog', JSON.stringify(donnes));
});
mouse_json = {};
chrome.tabs.captureVisibleTab(
null,
{},
function(dataUrl)
{
let data = {};
data['id'] = id;
data["data"] = dataUrl;
sendData('http://localhost:33333/saveimg', JSON.stringify(data));
console.log('Sent screenshot');
}
);
try {
chrome.tabs.executeScript(null, {
file: 'injection_script.js'
}, function() {
if (chrome.runtime.lastError) {
message.innerText = 'There was an error injecting script : \n' + chrome.runtime.lastError.message;
}
});
} catch(err) {
console.error(err);
}
}
}
});
I am also declaring on my manifest all the necessary permissions :
"all_urls", "activeTab", "storage", "tabs", "webRequest"
Is there anything I am doing wrong ? I haven't changed a single line to the code that was working last time I tested, so I am doubting the issue may not be from my code, but from the browser ?

Why is this call to IDBObjectStore.get() is resulting in confusing behavior?

I am trying to get the hang of using indexedDB to store data client side.
consider the following code:
function queryURL(message, sender)
{
chrome.contextMenus.removeAll();
var openRequest = indexedDB.open("Tags",1);
openRequest.onsuccess = function(event){
var queryURL = message['host'];
var db = event.target.result;
var objectStore = db.transaction("domains").objectStore("domains");
var query = objectStore.get(queryURL);
query.onsuccess = function(event){
alert(query.result);
delete query.result["domain"];
createMenuItems(query.result);
available_commands=request.result;
};
db.onerror = function(event){
console.log("an error bubbled up during a transaction.");
};
};
openRequest.onerror = function(event){
console.log("error opening DB");
};
}
I do not fully understand what should be happening in the query.
The result is the same whether or not the key that is queried for is in the database:
query.onsuccess() runs and query.result is undefined so the
code errors and exits as soon as I try to delete a key from
query.result.
If the key is not found, query.onsuccess() should not be
running, correct?
If the key is found, query.result should hold the object that
corresponds to that key, correct?
In case it helps, here is the code that I used to initialize the database:
const db_name="Tags";
var request = window.indexedDB.open(db_name, 1);
var tags = [
//codes: 0 - markdown wrap tag
// 1 - HTML wrap tag
// 2 - single tag
{ domain: "www.youtube.com",
bold:["*",0],
strikethrough:["-",0],
italic:["_",0]
},
{ domain: "www.stackoverflow.com",
bold:["<strong>",1],
italic:["<em>",1],
strikethrough:["<del>",1],
superscript:["<sup>",1],
subscript:["<sub>",1],
heading1:["<h1>",1],
heading2:["<h2>",1],
heading3:["<h3>",1],
blockquote:["<blockquote>",1],
code:["<code>",1],
newline:["<br>",2],
horizontal:["<hr>",2]
}
];
request.onerror = function(event) {
alert("Error opening the database");
};
request.onupgradeneeded = function(event) {
var db = event.target.result;
alert("I'm doing stuff!");
var objectStore = db.createObjectStore("domains", {keyPath: "domain" });
objectStore.createIndex("domain", "domain", { unique: true });
objectStore.transaction.onComplete = function(event) {
var domanStore=db.transaction("domains","readwrite").objectStore("domains");
for(var i in tags)
{
domainStore.add(tags[i]);
}
}
};
Here are some links to the resources I am using:
Using IndexedDB
IDBObjectStore
IDBRequest
Finding out that the result is empty or undefined is a successful query. So yes, you get onsuccess call with result === undefined.
onerror is only reserved for when something breaks, e.g. you supplied an invalid key.
From IDBObjectStore.get docs:
Note: This method produces the same result for: a) a record that doesn't exist in the database and b) a record that has an undefined value. To tell these situations apart, call the openCursor() method with the same key. That method provides a cursor if the record exists, and no cursor if it does not.
Yes. It is even more confusing when delete method return success, even if no record is deleted.
Since request error event is cancellable bubbling event, it is not feasible to invoke to error callback even if no record is found. If request is on error and error is not prevented, its transaction will be aborted and indexedDB.onerror will be called as well. So invoking success with undefined result is still better than invoking error.

YouTube Asynchronous function

I've been messing with the YouTube Javascript API recently, but I've run into a problem (as always!).
I need a function to return an array of information about a video:
[0: Title, 1: Description, 2: Publish Date, 3: Thumbnail URL]
The function takes the id of a video then does a video.list with that id. Here is that function:
function getVidInfo(VidId){
var vidRequest;
var vidRequestResponse;
var returnArray = new Array(4);
vidRequest = gapi.client.youtube.videos.list({
part: 'snippet',
id: VidId
});
vidRequest.execute(function(response){
if(response.pageInfo.totalResults != 0) {
returnArray[0] = response.result.items[0].snippet.title;
returnArray[1] = response.result.items[0].snippet.description;
returnArray[2] = response.result.items[0].snippet.publishedAt;
//Check for HD thumbnail
if (response.result.items[0].snippet.thumbnails.maxres.url){
returnArray[3] = response.result.items[0].snippet.thumbnails.maxres.url
}
else {
returnArray[3] = response.result.items[0].snippet.thumbnails.standard.url;
}
console.log(returnArray); //Will log desired return array
}
});
return returnArray; //Not returning desired array
}
As you can see from the comments the array is being set correctly however it's not returning that value.
What have I tried?
Using an external variable being set from a function called in vidRequest.execute()
Returning from vidRequest.execute()
Putting response into a variable and then assigning the array (Gave me an error about pageInfo being undefined)
Notes
It appears to be an asynchronous problem
I need to keep getVidInfo()
It definetely gets called when the Google API loads
It appears to work on the initial load, but a refresh breaks it
All info is logged to the console at the moment
Full Code
index.html
<!DOCTYPE html>
<html>
<head>
<title>YT Test</title>
<!--My Scripts-->
<script src="test.js"></script>
</head>
<body>
<!-- Load google api last-->
<script src="https://apis.google.com/js/client.js?onload=googleApiClientReady"> </script>
</body>
</html>
test.js
var apiKey = "[YOUR API KEY]"; //I did set this to my API key
var latestVidUrl;
var request;
var vidId;
var vidInfo;
function googleApiClientReady() {
console.log("Google api loaded");
gapi.client.setApiKey(apiKey);
gapi.client.load('youtube', 'v3', function() {
request = gapi.client.youtube.search.list({
part: 'id',
channelId: 'UCOYWgypDktXdb-HfZnSMK6A',
maxResults: 1,
type: 'video',
order: 'date'
});
request.execute(function(response) {
if(response.pageInfo.totalResults != 0) {
vidId = response.result.items[0].id.videoId;
//console.log(vidId);
vidInfo = getVidInfo(vidId);
console.log(vidInfo);
}
});
});
}
function getEmbedCode(id){
var baseURL = "http://www.youtube.com/watch?v="
return baseURL + id.toString();
}
function getVidInfo(VidId){
var vidRequest;
var vidRequestResponse;
var returnArray = new Array(4);
vidRequest = gapi.client.youtube.videos.list({
part: 'snippet',
id: VidId
});
vidRequest.execute(function(response){
if(response.pageInfo.totalResults != 0) {
returnArray[0] = response.result.items[0].snippet.title;
returnArray[1] = response.result.items[0].snippet.description;
returnArray[2] = response.result.items[0].snippet.publishedAt;
//Check for HD thumbnail
if (response.result.items[0].snippet.thumbnails.maxres.url){
returnArray[3] = response.result.items[0].snippet.thumbnails.maxres.url
}
else {
returnArray[3] = response.result.items[0].snippet.thumbnails.standard.url;
}
console.log(returnArray); //Will log desired return array
}
});
return returnArray; //Not returning desired array
}
I think the problem is that you are returning returnArray when it might not be handled yet. To clarify what I mean, Even though you have return returnArray at the end, the actual request is still be handled, but the code keeps going anyway. So when it finally gets a response, and handles the code, it writes it correctly to the log, but the function has already returned returnArray earlier. Without testing if this works, you could probably just add a polling function to wait until returnArray is not null, as long as you never expect it to be null. Maybe something like:
while(returnArray == null) {
; }
return returnArray;
I'll just edit this to clarify what I mean:
function getVidInfo(VidId){
var vidRequest;
var vidRequestResponse;
var returnArray = new Array(4);
vidRequest = gapi.client.youtube.videos.list({
part: 'snippet',
id: VidId
});
vidRequest.execute(function(response){
if(response.pageInfo.totalResults != 0) {
returnArray[0] = response.result.items[0].snippet.title;
returnArray[1] = response.result.items[0].snippet.description;
returnArray[2] = response.result.items[0].snippet.publishedAt;
//Check for HD thumbnail
if (response.result.items[0].snippet.thumbnails.maxres.url){
returnArray[3] = response.result.items[0].snippet.thumbnails.maxres.url
}
else {
returnArray[3] = response.result.items[0].snippet.thumbnails.standard.url;
}
console.log(returnArray); //Will log desired return array
}
});
while(returnArray == null) { //Create busy loop to wait for value
; }
return returnArray;
}
The execute function is asynchronous; thus, it hasn't completed by the time you're returning the returnArray array, and so an empty array gets sent back instead (if you have the console open you'll see that's the case, where the empty array comes back and gets logged, and then a second or so later the logging within the callback happens). This is one of the biggest obstacles in asynchronous programming, and with the YouTube APIs it used to be that the only way around it was to nest your callbacks in multiple levels (i.e. don't have it as a separate function that returns a value) -- or what I like to affectionately term callback inception. So you could go that route (where you move all the code from your getVidInfo function up into the callback from the request where you get the ID), but that will get very messy ... and luckily the API client very recently introduced features that make solving this problem a whole lot easier -- the gapi client is now Promises/A+ compliant.
So basically, all request objects can now return a Promise object instead of utilize a callback function, and you can chain them all together so they all get processed and resolved in the order you need them to (note that this promise object does very slightly change the json structure of the response packet, where parameters such as pageInfo are children of the result attribute rather than siblings -- you'll see in the sample code below what I mean). This will also greatly simplify your code, so you could do something like this:
var apiKey = "[YOUR API KEY]";
function googleApiClientReady() {
console.log("Google api loaded");
gapi.client.setApiKey(apiKey);
gapi.client.load('youtube', 'v3', function() {
var request = gapi.client.youtube.search.list({
part: 'id',
channelId: 'UCOYWgypDktXdb-HfZnSMK6A',
maxResults: 1,
type: 'video',
order: 'date'
}).then(function(response) {
if(response.result.pageInfo.totalResults != 0) { // note how pageInfo is now a child of response.result ... this is because the promise object is structured a bit differently
return response.result.items[0].id.videoId;
}
}).then(function(vidId) {
return gapi.client.youtube.videos.list({
part: 'snippet',
id: vidId
});
}).then(function(response) {
var returnArray=Array();
if(response.result.pageInfo.totalResults != 0) {
returnArray[0] = response.result.items[0].snippet.title;
returnArray[1] = response.result.items[0].snippet.description;
returnArray[2] = response.result.items[0].snippet.publishedAt;
//Check for HD thumbnail
if (response.result.items[0].snippet.thumbnails.maxres.url){
returnArray[3] = response.result.items[0].snippet.thumbnails.maxres.url;
}
else {
returnArray[3] = response.result.items[0].snippet.thumbnails.standard.url;
}
}
return returnArray;
}).then(function(returnArray) {
console.log(returnArray);
});
});
}
This architecture could also greatly help with error handling, as you could construct additional anonymous functions to pass as the 2nd argument in each then call to be executed when the API throws an error of some sort. Because each of the calls returns a promise, you can, in the final call, use returnArray however you need, and it will wait until all the pieces are resolved before executing.

Getting result from querying sqlite db in the add-on script to be submitted to the content script

I am writting a modest firefox add-on and I have some problems getting the results used inside the "flow" of the add-on script.
I have the code taking care of querying a sqlite database as a module but I don't know how to create a callback inside of it so that the pagemod in the add-on script can use it and pass it to the content script.
Basically here is what I have:
main.js :
var pageMod = require("sdk/page-mod");
var self = require("sdk/self");
var myDbScript = require('./myDbScript');
pageMod.PageMod({
include: "*.example.com/*",
contentScriptFile: [self.data.url('jquery-1.10.2.min.js'),
self.data.url('myContentScript.js')],
onAttach: function(worker) {
// Query the database on behalf of the content script
worker.port.on('queryTheDB', function(message) {
// Get the data from the DB (é is some test value here)
// Not working because asynchronous querying of the DB
var resultFromDB = myDbScript.getResult(2);
// Send the result to the content script
worker.port.emit('hereIsYourResult', resultFromDB);
});
}
});
myDBScript.js
// Get required components
var {components} = require("chrome");
components.utils.import("resource://gre/modules/FileUtils.jsm");
components.utils.import("resource://gre/modules/Services.jsm");
// Some code to get the DB
// Create statement to retrieve country based on the IP
var statement = dbConnection.createStatement("SELECT col1, col2 FROM table WHERE col1 = :given_col1");
function getResult(submittedValue) {
// Bind parameters
statement.params.given_col1 = submittedValue;
// Execute
statement.executeAsync({
handleResult: function(aResultSet) {
for (let row = aResultSet.getNextRow();
row;
row = aResultSet.getNextRow()) {
var resultFromDB = row.getResultByName("col2");
}
},
handleError: function(aError) {
print("Error: " + aError.message);
return 'error';
},
handleCompletion: function(aReason) {
if (aReason != components.interfaces.mozIStorageStatementCallback.REASON_FINISHED) {
print("Query canceled or aborted!");
return 'canceledOrAborted';
} else {
// Sending the result to the add-on script so that it can
// pass it to the content script
notifyingTheAddonScript(resultFromDB);
}
}
});
}
// Enable the use of the getResult function
exports.getResult = getResult;
The thing is that I don't see how to have the addon script be aware that the result is ready. Please bear with me, I am a noob at this...
Since I don't have the full source, I cannot test. So you'll have to fix any I made errors yourself ;)
First, lets add a callback.
// #param {function(result, error)} callback
// Called upon query completion.
// if |error| is a string, then the query failed.
// Else |result| will contain an array of values.
function getResult(submittedValue, callback) { // changed
// Bind parameters
statement.params.given_col1 = submittedValue;
var rv = [], err = null; // added
// Execute
statement.executeAsync({
handleResult: function(aResultSet) {
for (let row = aResultSet.getNextRow();
row;
row = aResultSet.getNextRow()) {
rv.push(row.getResultByName("col2")); // changed
}
},
handleError: function(aError) {
print("Error: " + aError.message);
err = aError.message; // changed
},
handleCompletion: function(aReason) {
if (aReason != components.interfaces.mozIStorageStatementCallback.REASON_FINISHED) {
print("Query canceled or aborted!");
err = err || 'canceled or aborted'; // changed
}
callback(err ? null : rv, err); // replaced
}
});
}
Lets use this stuff now in the pagemod
onAttach: function(worker) {
// Query the database on behalf of the content script
worker.port.on('queryTheDB', function(message) {
// Get the data from the DB (é is some test value here)
// Not working because asynchronous querying of the DB
myDbScript.getResult(2, function callback(result, error) {
if (error) {
worker.port.emit("hereIsYourError", error);
return;
}
worker.port.emit("hereIsYourResult", result);
});
});
}
You might want to take some precautions not to fire multiple queries. While it would be OK to do so, it might hurt performance ;)
Since our callback already looks kinda like a promise, it might actually be a good idea to use promises, maybe even with the Sqlite.jsm module and some Task.jsm magic.

Chrome Extension with Database API interface

I want to update a div with a list of anchors that I generate from a local database in chrome. It's pretty simple stuff, but as soon as I try to add the data to the main.js file via a callback everything suddenly becomes undefined. Or the array length is set to 0. ( When it's really 18. )
Initially, I tried to install it into a new array and pass it back that way.
Is there a setting that I need to specify in the chrome manifest.json in order to allow for communication with the database API? I've checked, but all I've been able to find was 'unlimited storage'
The code is as follows:
window.main = {};
window.main.classes = {};
(function(awe){
awe.Data = function(opts){
opts = opts || new Object();
return this.init(opts);
};
awe.Data.prototype = {
init:function(opts){
var self = this;
self.modified = true;
var db = self.db = openDatabase("buddy","1.0","LocalDatabase",200000);
db.transaction(function(tx){
tx.executeSql("CREATE TABLE IF NOT EXISTS listing ( name TEXT UNIQUE, url TEXT UNIQUE)",[],function(tx,rs){
$.each(window.rr,function(index,item){
var i = "INSERT INTO listing (name,url)VALUES('"+item.name+"','"+item.url+"')";
tx.executeSql(i,[],null,null);
});
},function(tx,error){
});
});
self._load()
return this;
},
add:function(item){
var self = this;
self.modified = true;
self.db.transaction(function(tx){
tx.executeSql("INSERT INTO listing (name,url)VALUES(?,?)",[item.name,item.url],function(tx,rs){
//console.log('success',tx,rs)
},function(tx,error){
//console.log('error',error)
})
});
self._load()
},
remove:function(item){
var self = this;
self.modified = true;
self.db.transaction(function(tx){
tx.executeSql("DELETE FROM listing where name='"+item.name+"'",[],function(tx,rs){
//console.log('success',tx,rs)
},function(tx,error){
//console.log('error',tx,error);
});
});
self._load()
},
_load:function(callback){
var self = this;
if(!self.modified)
return;
self.data = new Array();
self.db.transaction(function(tx){
tx.executeSql('SELECT name,url FROM listing',[],function(tx,rs){
console.log(callback)
for(var i = 0; i<rs.rows.length;i++)
{
callback(rs.rows.item(i).name,rs.rows.item(i).url)
// var row = rs.rows.item(i)
// var n = new Object()
// n['name'] = row['name'];
// n['url'] = row['url'];
}
},function(tx,error){
//console.log('error',tx,error)
})
})
self.modified = false
},
all:function(cb){
this._load(cb)
},
toString:function(){
return 'main.Database'
}
}
})(window.main.classes);
And the code to update the list.
this.database.all(function(name,url){
console.log('name','url')
console.log(name,url)
var data = []
$.each(data,function(index,item){
try{
var node = $('<div > '+item.name + '</div>');
self.content.append(node);
node.unbind();
node.bind('click',function(evt){
var t = $(evt.target).attr('href');
chrome.tabs.create({
"url":t
},function(evt){
self._tab_index = evt.index
});
});
}catch(e){
console.log(e)
}
})
});
From looking at your code above, I notice you are executing "self._load()" at the end of each function in your API. The HTML5 SQL Database is asynchronous, you can never guarantee the result. In this case, I would assume the result will always be 0 or random because it will be a race condition.
I have done something similar in my fb-exporter extension, feel free to see how I have done it https://github.com/mohamedmansour/fb-exporter/blob/master/js/database.js
To solve a problem like this, did you check the Web Inspector and see if any errors occurs in the background page. I assume this is all in a background page eh? Try to see if any error occurs, if not, I believe your encountering a race condition. Just move the load within the callback and it should properly call the load.
Regarding your first question with the unlimited storage manifest attribute, you don't need it for this case, that shouldn't be the issue. The limit of web databases is 5MB (last I recall, it might have changed), if your using a lot of data manipulation, then you use that attribute.
Just make sure you can guarantee the this.database.all is running after the database has been initialized.

Categories

Resources