public
Last active

Shim that converts Chrome's SetVersion to the standard IndexedDB onupgradeneeded events

  • Download Gist
IndexedDB.setVersionShim.js
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
////////////////////////////////////////////////////////////////////////////////////////////////////
var openReqShim = function(dbName, version){
var me = this;
var IDBRequest = function(){
this.onsuccess = this.onerror = this.onblocked = this.onupgradeneeded = null;
};
function copyReq(req){
req = req || dbOpenReq;
for (var key in req) {
if (typeof result[key] === "undefined") {
result[key] = req[key];
}
}
}
function callback(fn, context, argArray, func){
//window.setTimeout(function(){
(typeof context[fn] === "function") && context[fn].apply(context, argArray);
(typeof func === "function") && func();
//}, 1);
}
var indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
var dbOpenReq = version ? indexedDB.open(dbName, version) : indexedDB.open(dbName);
var result = new IDBRequest();
dbOpenReq.onsuccess = function(e){
copyReq();
var db = dbOpenReq.result;
if (typeof db.setVersion === "function") {
var oldVersion = parseInt(db.version || 1, 10);
var newVersion = typeof version === "undefined" ? oldVersion : parseInt(version, 10);
if (oldVersion < newVersion) {
var versionReq = db.setVersion(version);
versionReq.onsuccess = function(upgradeEvent){
result.transaction = versionReq.result;
var event = new Event("upgradeneeded");
event.oldVersion = oldVersion;
event.newVersion = newVersion;
for (key in upgradeEvent) {
if (key !== "type") {
event[key] = upgradeEvent[key];
}
}
callback("onupgradeneeded", result, [event]);
// Version transaction is now complete, to open ordinary transaction
versionReq.result.db.close();
//console.log("Database closed, and will try to open again, with same version");
var newDbOpenReq = indexedDB.open(dbName);
delete result.transaction;
delete result.result;
newDbOpenReq.onsuccess = function(e){
//console.log("DB Opened without version change", newDbOpenReq.result);
copyReq(newDbOpenReq);
callback("onsuccess", result, [e], function(){
newDbOpenReq.result.close();
});
newDbOpenReq.result.close();
};
newDbOpenReq.onerror = function(e){
copyReq(newDbOpenReq);
callback("onerror", result, [e], function(){
//console.log("Closed database in newRequest on error", newDbOpenReq);
newDbOpenReq.result.close();
});
};
newDbOpenReq.onblocked = function(e){
//console.log("DB Blocked without version change", newDbOpenReq.result);
copyReq(newDbOpenReq);
callback("onblocked", result, [e], function(){
//console.log("Closed database in newRequest on blocked", newDbOpenReq);
newDbOpenReq.result.close();
});
};
};
versionReq.onerror = function(){
callback("onerror", result, [e]);
versionReq.result.close();
};
versionReq.onblocked = function(e){
// This always gets called, resulting the blocking the DB upgrade
//console.log("Version transaction blocked, so calling the on blocked method");
callback("onblocked", result, [e]);
};
} else if (oldVersion === newVersion) {
callback("onsuccess", result, [e]);
db.close();
} else {
callback("onerror", result, [e]);
db.close();
}
} else {
callback("onsuccess", result, [e]);
}
};
dbOpenReq.onerror = function(e){
copyReq();
//console.log("Error", dbOpenReq);
callback("onerror", result, [e]);
};
dbOpenReq.onblocked = function(e){
copyReq();
callback("onblocked", result, [e]);
};
dbOpenReq.onupgradeneeded = function(e){
copyReq();
if (typeof result["onupgradeneeded"] === "function") {
result["onupgradeneeded"](e);
}
};
return result;
}

I think there's something broken with this shim under Chrome 20. I'm not sure if it's just 20 or earlier as well, though. I have this test case. You can flip useShim to true or false. It should wipe the old database, create the new one, create an object store, then add data to it:

var DB = 'db';
var OS = 'test';
var ver = 10;
var db;
var os;

var useShim = true;


window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
for (var f in window) {
  if (window.hasOwnProperty(f)) {
    if (f.substr(0, 'webkitIDB'.length) === 'webkitIDB') {
      window[f.slice('webkit'.length, f.length)] = window[f];
    }
  }
}

var req = window.indexedDB.getDatabaseNames();

req.onsuccess = function (e) {
  if (e.target.result.length !== 0) {
    console.log('deleting db');
    var req = window.indexedDB.deleteDatabase(DB);
    req.onsuccess = function () {
      console.log('db deleted - running test');
      setTimeout(runTest, 100);
    }
  }
  else {
    console.log('db not found - running test');
    setTimeout(runTest, 100);
  }
};


function runTest() {
  var openReq = useShim ? openReqShim(DB, ver) : window.indexedDB.open(DB, ver);

  openReq.onupgradeneeded = function (e) {
    console.log('onupgradeneeded');

    db = useShim ? this.result : e.currentTarget.source;

    if (db.objectStoreNames.length === 0) {
      console.log('creating object store');

      db.createObjectStore(OS, {
        "keyPath":"akey"
      });
    }

    console.log('createObjectStore success');

    setTimeout(tryAdd, 1000);
  };

  openReq.onsuccess = function (e) {
    db = e.currentTarget.result;

    console.log("Database Opened successfully");

    if (!useShim) {
      var request = db.setVersion(ver);

      request.onsuccess = function () {
        console.log("setversion success");

        //if (ver != db.version) {
        if (db.objectStoreNames.length === 0) {
          openReq.onupgradeneeded.apply(this, arguments);
        }
      };
    }

  };

  openReq.onerror = function (e) {
    console.log("Database NOT Opened successfully");
  };
  openReq.onblocked = function (e) {
    console.log("Database blocked called");
  };
}

function tryAdd() {
  console.log('Getting transaction');
  var trans = db.transaction([OS], window.IDBTransaction.READ_WRITE);

  trans.oncomplete = function () {
    console.log('oncomplete', arguments);
  };

  trans.onerror = function () {
    console.log('onerror', arguments);
  };

  trans.onabort = function () {
    console.log('onabort', arguments);
  };

  console.log('Getting objectstore');
  os = trans.objectStore(OS);

  console.log('got store:', os);

  console.log('Adding key');
  os.add({"akey":(new Date()).getTime()});

  console.log('Done');
}

If you set useShim=false, it works fine. With useShim=true, I get this error:
Uncaught Error: NOT_ALLOWED_ERR: DOM IDBDatabase Exception 6
when trying to get a transaction in tryAdd.

It seems like maybe it's leaving it in a bad state. Any ideas?

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.