From 98cd3e6da7ca7bf0902b0dd16f31f1c1ad0106f1 Mon Sep 17 00:00:00 2001 From: "David Humphrey (:humph) david.humphrey@senecacollege.ca" Date: Mon, 23 Jun 2014 13:09:02 -0400 Subject: [PATCH] Fix #228: Uncaught [Filer Error] Your browser doesn't support IndexedDB or WebSQL. --- src/providers/indexeddb.js | 235 +++++++++++++++--------------- src/providers/websql.js | 287 ++++++++++++++++++------------------- tests/lib/indexeddb.js | 87 ++++++----- tests/lib/websql.js | 72 +++++----- 4 files changed, 334 insertions(+), 347 deletions(-) diff --git a/src/providers/indexeddb.js b/src/providers/indexeddb.js index 417960e..1b5d8ca 100644 --- a/src/providers/indexeddb.js +++ b/src/providers/indexeddb.js @@ -1,130 +1,127 @@ -(function(global) { - var FILE_SYSTEM_NAME = require('../constants.js').FILE_SYSTEM_NAME; - var FILE_STORE_NAME = require('../constants.js').FILE_STORE_NAME; - var IDB_RW = require('../constants.js').IDB_RW; - var IDB_RO = require('../constants.js').IDB_RO; - var Errors = require('../errors.js'); +var FILE_SYSTEM_NAME = require('../constants.js').FILE_SYSTEM_NAME; +var FILE_STORE_NAME = require('../constants.js').FILE_STORE_NAME; +var IDB_RW = require('../constants.js').IDB_RW; +var IDB_RO = require('../constants.js').IDB_RO; +var Errors = require('../errors.js'); - var indexedDB = global.indexedDB || - global.mozIndexedDB || - global.webkitIndexedDB || - global.msIndexedDB; +var indexedDB = global.indexedDB || + global.mozIndexedDB || + global.webkitIndexedDB || + global.msIndexedDB; - function IndexedDBContext(db, mode) { - var transaction = db.transaction(FILE_STORE_NAME, mode); - this.objectStore = transaction.objectStore(FILE_STORE_NAME); +function IndexedDBContext(db, mode) { + var transaction = db.transaction(FILE_STORE_NAME, mode); + this.objectStore = transaction.objectStore(FILE_STORE_NAME); +} +IndexedDBContext.prototype.clear = function(callback) { + try { + var request = this.objectStore.clear(); + request.onsuccess = function(event) { + callback(); + }; + request.onerror = function(error) { + callback(error); + }; + } catch(e) { + callback(e); } - IndexedDBContext.prototype.clear = function(callback) { - try { - var request = this.objectStore.clear(); - request.onsuccess = function(event) { - callback(); - }; - request.onerror = function(error) { - callback(error); - }; - } catch(e) { - callback(e); - } - }; - IndexedDBContext.prototype.get = function(key, callback) { - try { - var request = this.objectStore.get(key); - request.onsuccess = function onsuccess(event) { - var result = event.target.result; - callback(null, result); - }; - request.onerror = function onerror(error) { - callback(error); - }; - } catch(e) { - callback(e); - } - }; - IndexedDBContext.prototype.put = function(key, value, callback) { - try { - var request = this.objectStore.put(value, key); - request.onsuccess = function onsuccess(event) { - var result = event.target.result; - callback(null, result); - }; - request.onerror = function onerror(error) { - callback(error); - }; - } catch(e) { - callback(e); - } - }; - IndexedDBContext.prototype.delete = function(key, callback) { - try { - var request = this.objectStore.delete(key); - request.onsuccess = function onsuccess(event) { - var result = event.target.result; - callback(null, result); - }; - request.onerror = function(error) { - callback(error); - }; - } catch(e) { - callback(e); - } - }; - - - function IndexedDB(name) { - this.name = name || FILE_SYSTEM_NAME; - this.db = null; +}; +IndexedDBContext.prototype.get = function(key, callback) { + try { + var request = this.objectStore.get(key); + request.onsuccess = function onsuccess(event) { + var result = event.target.result; + callback(null, result); + }; + request.onerror = function onerror(error) { + callback(error); + }; + } catch(e) { + callback(e); } - IndexedDB.isSupported = function() { - return !!indexedDB; - }; +}; +IndexedDBContext.prototype.put = function(key, value, callback) { + try { + var request = this.objectStore.put(value, key); + request.onsuccess = function onsuccess(event) { + var result = event.target.result; + callback(null, result); + }; + request.onerror = function onerror(error) { + callback(error); + }; + } catch(e) { + callback(e); + } +}; +IndexedDBContext.prototype.delete = function(key, callback) { + try { + var request = this.objectStore.delete(key); + request.onsuccess = function onsuccess(event) { + var result = event.target.result; + callback(null, result); + }; + request.onerror = function(error) { + callback(error); + }; + } catch(e) { + callback(e); + } +}; - IndexedDB.prototype.open = function(callback) { - var that = this; - // Bail if we already have a db open - if( that.db ) { - callback(null, false); - return; +function IndexedDB(name) { + this.name = name || FILE_SYSTEM_NAME; + this.db = null; +} +IndexedDB.isSupported = function() { + return !!indexedDB; +}; + +IndexedDB.prototype.open = function(callback) { + var that = this; + + // Bail if we already have a db open + if( that.db ) { + callback(null, false); + return; + } + + // Keep track of whether we're accessing this db for the first time + // and therefore needs to get formatted. + var firstAccess = false; + + // NOTE: we're not using versioned databases. + var openRequest = indexedDB.open(that.name); + + // If the db doesn't exist, we'll create it + openRequest.onupgradeneeded = function onupgradeneeded(event) { + var db = event.target.result; + + if(db.objectStoreNames.contains(FILE_STORE_NAME)) { + db.deleteObjectStore(FILE_STORE_NAME); } + db.createObjectStore(FILE_STORE_NAME); - // Keep track of whether we're accessing this db for the first time - // and therefore needs to get formatted. - var firstAccess = false; - - // NOTE: we're not using versioned databases. - var openRequest = indexedDB.open(that.name); - - // If the db doesn't exist, we'll create it - openRequest.onupgradeneeded = function onupgradeneeded(event) { - var db = event.target.result; - - if(db.objectStoreNames.contains(FILE_STORE_NAME)) { - db.deleteObjectStore(FILE_STORE_NAME); - } - db.createObjectStore(FILE_STORE_NAME); - - firstAccess = true; - }; - - openRequest.onsuccess = function onsuccess(event) { - that.db = event.target.result; - callback(null, firstAccess); - }; - openRequest.onerror = function onerror(error) { - callback(new Errors.EINVAL('IndexedDB cannot be accessed. If private browsing is enabled, disable it.')); - }; - }; - IndexedDB.prototype.getReadOnlyContext = function() { - // Due to timing issues in Chrome with readwrite vs. readonly indexeddb transactions - // always use readwrite so we can make sure pending commits finish before callbacks. - // See https://github.com/js-platform/filer/issues/128 - return new IndexedDBContext(this.db, IDB_RW); - }; - IndexedDB.prototype.getReadWriteContext = function() { - return new IndexedDBContext(this.db, IDB_RW); + firstAccess = true; }; - module.exports = IndexedDB; + openRequest.onsuccess = function onsuccess(event) { + that.db = event.target.result; + callback(null, firstAccess); + }; + openRequest.onerror = function onerror(error) { + callback(new Errors.EINVAL('IndexedDB cannot be accessed. If private browsing is enabled, disable it.')); + }; +}; +IndexedDB.prototype.getReadOnlyContext = function() { + // Due to timing issues in Chrome with readwrite vs. readonly indexeddb transactions + // always use readwrite so we can make sure pending commits finish before callbacks. + // See https://github.com/js-platform/filer/issues/128 + return new IndexedDBContext(this.db, IDB_RW); +}; +IndexedDB.prototype.getReadWriteContext = function() { + return new IndexedDBContext(this.db, IDB_RW); +}; -}(this)); +module.exports = IndexedDB; diff --git a/src/providers/websql.js b/src/providers/websql.js index c8aa636..ba09e13 100644 --- a/src/providers/websql.js +++ b/src/providers/websql.js @@ -1,161 +1,158 @@ -(function(global) { - var FILE_SYSTEM_NAME = require('../constants.js').FILE_SYSTEM_NAME; - var FILE_STORE_NAME = require('../constants.js').FILE_STORE_NAME; - var WSQL_VERSION = require('../constants.js').WSQL_VERSION; - var WSQL_SIZE = require('../constants.js').WSQL_SIZE; - var WSQL_DESC = require('../constants.js').WSQL_DESC; - var u8toArray = require('../shared.js').u8toArray; - var Errors = require('../errors.js'); +var FILE_SYSTEM_NAME = require('../constants.js').FILE_SYSTEM_NAME; +var FILE_STORE_NAME = require('../constants.js').FILE_STORE_NAME; +var WSQL_VERSION = require('../constants.js').WSQL_VERSION; +var WSQL_SIZE = require('../constants.js').WSQL_SIZE; +var WSQL_DESC = require('../constants.js').WSQL_DESC; +var u8toArray = require('../shared.js').u8toArray; +var Errors = require('../errors.js'); - function WebSQLContext(db, isReadOnly) { - var that = this; - this.getTransaction = function(callback) { - if(that.transaction) { - callback(that.transaction); - return; +function WebSQLContext(db, isReadOnly) { + var that = this; + this.getTransaction = function(callback) { + if(that.transaction) { + callback(that.transaction); + return; + } + // Either do readTransaction() (read-only) or transaction() (read/write) + db[isReadOnly ? 'readTransaction' : 'transaction'](function(transaction) { + that.transaction = transaction; + callback(transaction); + }); + }; +} +WebSQLContext.prototype.clear = function(callback) { + function onError(transaction, error) { + callback(error); + } + function onSuccess(transaction, result) { + callback(null); + } + this.getTransaction(function(transaction) { + transaction.executeSql("DELETE FROM " + FILE_STORE_NAME + ";", + [], onSuccess, onError); + }); +}; +WebSQLContext.prototype.get = function(key, callback) { + function onSuccess(transaction, result) { + // If the key isn't found, return null + var value = result.rows.length === 0 ? null : result.rows.item(0).data; + try { + if(value) { + value = JSON.parse(value); + // Deal with special-cased flattened typed arrays in WebSQL (see put() below) + if(value.__isUint8Array) { + value = new Uint8Array(value.__array); + } } - // Either do readTransaction() (read-only) or transaction() (read/write) - db[isReadOnly ? 'readTransaction' : 'transaction'](function(transaction) { - that.transaction = transaction; - callback(transaction); - }); + callback(null, value); + } catch(e) { + callback(e); + } + } + function onError(transaction, error) { + callback(error); + } + this.getTransaction(function(transaction) { + transaction.executeSql("SELECT data FROM " + FILE_STORE_NAME + " WHERE id = ?;", + [key], onSuccess, onError); + }); +}; +WebSQLContext.prototype.put = function(key, value, callback) { + // We do extra work to make sure typed arrays survive + // being stored in the db and still get the right prototype later. + if(Object.prototype.toString.call(value) === "[object Uint8Array]") { + value = { + __isUint8Array: true, + __array: u8toArray(value) }; } - WebSQLContext.prototype.clear = function(callback) { - function onError(transaction, error) { - callback(error); - } - function onSuccess(transaction, result) { - callback(null); - } - this.getTransaction(function(transaction) { - transaction.executeSql("DELETE FROM " + FILE_STORE_NAME + ";", - [], onSuccess, onError); - }); - }; - WebSQLContext.prototype.get = function(key, callback) { - function onSuccess(transaction, result) { - // If the key isn't found, return null - var value = result.rows.length === 0 ? null : result.rows.item(0).data; - try { - if(value) { - value = JSON.parse(value); - // Deal with special-cased flattened typed arrays in WebSQL (see put() below) - if(value.__isUint8Array) { - value = new Uint8Array(value.__array); - } - } - callback(null, value); - } catch(e) { - callback(e); - } - } - function onError(transaction, error) { - callback(error); - } - this.getTransaction(function(transaction) { - transaction.executeSql("SELECT data FROM " + FILE_STORE_NAME + " WHERE id = ?;", - [key], onSuccess, onError); - }); - }; - WebSQLContext.prototype.put = function(key, value, callback) { - // We do extra work to make sure typed arrays survive - // being stored in the db and still get the right prototype later. - if(Object.prototype.toString.call(value) === "[object Uint8Array]") { - value = { - __isUint8Array: true, - __array: u8toArray(value) - }; - } - value = JSON.stringify(value); - function onSuccess(transaction, result) { - callback(null); - } - function onError(transaction, error) { - callback(error); - } - this.getTransaction(function(transaction) { - transaction.executeSql("INSERT OR REPLACE INTO " + FILE_STORE_NAME + " (id, data) VALUES (?, ?);", - [key, value], onSuccess, onError); - }); - }; - WebSQLContext.prototype.delete = function(key, callback) { - function onSuccess(transaction, result) { - callback(null); - } - function onError(transaction, error) { - callback(error); - } - this.getTransaction(function(transaction) { - transaction.executeSql("DELETE FROM " + FILE_STORE_NAME + " WHERE id = ?;", - [key], onSuccess, onError); - }); - }; - - - function WebSQL(name) { - this.name = name || FILE_SYSTEM_NAME; - this.db = null; + value = JSON.stringify(value); + function onSuccess(transaction, result) { + callback(null); } - WebSQL.isSupported = function() { - return !!global.openDatabase; - }; + function onError(transaction, error) { + callback(error); + } + this.getTransaction(function(transaction) { + transaction.executeSql("INSERT OR REPLACE INTO " + FILE_STORE_NAME + " (id, data) VALUES (?, ?);", + [key, value], onSuccess, onError); + }); +}; +WebSQLContext.prototype.delete = function(key, callback) { + function onSuccess(transaction, result) { + callback(null); + } + function onError(transaction, error) { + callback(error); + } + this.getTransaction(function(transaction) { + transaction.executeSql("DELETE FROM " + FILE_STORE_NAME + " WHERE id = ?;", + [key], onSuccess, onError); + }); +}; - WebSQL.prototype.open = function(callback) { - var that = this; - // Bail if we already have a db open - if(that.db) { - callback(null, false); - return; +function WebSQL(name) { + this.name = name || FILE_SYSTEM_NAME; + this.db = null; +} +WebSQL.isSupported = function() { + return !!global.openDatabase; +}; + +WebSQL.prototype.open = function(callback) { + var that = this; + + // Bail if we already have a db open + if(that.db) { + callback(null, false); + return; + } + + var db = global.openDatabase(that.name, WSQL_VERSION, WSQL_DESC, WSQL_SIZE); + if(!db) { + callback("[WebSQL] Unable to open database."); + return; + } + + function onError(transaction, error) { + if (error.code === 5) { + callback(new Errors.EINVAL('WebSQL cannot be accessed. If private browsing is enabled, disable it.')); } + callback(error); + } + function onSuccess(transaction, result) { + that.db = db; - var db = global.openDatabase(that.name, WSQL_VERSION, WSQL_DESC, WSQL_SIZE); - if(!db) { - callback("[WebSQL] Unable to open database."); - return; + function gotCount(transaction, result) { + var firstAccess = result.rows.item(0).count === 0; + callback(null, firstAccess); } - function onError(transaction, error) { - if (error.code === 5) { - callback(new Errors.EINVAL('WebSQL cannot be accessed. If private browsing is enabled, disable it.')); - } callback(error); } - function onSuccess(transaction, result) { - that.db = db; + // Keep track of whether we're accessing this db for the first time + // and therefore needs to get formatted. + transaction.executeSql("SELECT COUNT(id) AS count FROM " + FILE_STORE_NAME + ";", + [], gotCount, onError); + } - function gotCount(transaction, result) { - var firstAccess = result.rows.item(0).count === 0; - callback(null, firstAccess); - } - function onError(transaction, error) { - callback(error); - } - // Keep track of whether we're accessing this db for the first time - // and therefore needs to get formatted. - transaction.executeSql("SELECT COUNT(id) AS count FROM " + FILE_STORE_NAME + ";", - [], gotCount, onError); + // Create the table and index we'll need to store the fs data. + db.transaction(function(transaction) { + function createIndex(transaction) { + transaction.executeSql("CREATE INDEX IF NOT EXISTS idx_" + FILE_STORE_NAME + "_id" + + " on " + FILE_STORE_NAME + " (id);", + [], onSuccess, onError); } + transaction.executeSql("CREATE TABLE IF NOT EXISTS " + FILE_STORE_NAME + " (id unique, data TEXT);", + [], createIndex, onError); + }); +}; +WebSQL.prototype.getReadOnlyContext = function() { + return new WebSQLContext(this.db, true); +}; +WebSQL.prototype.getReadWriteContext = function() { + return new WebSQLContext(this.db, false); +}; - // Create the table and index we'll need to store the fs data. - db.transaction(function(transaction) { - function createIndex(transaction) { - transaction.executeSql("CREATE INDEX IF NOT EXISTS idx_" + FILE_STORE_NAME + "_id" + - " on " + FILE_STORE_NAME + " (id);", - [], onSuccess, onError); - } - transaction.executeSql("CREATE TABLE IF NOT EXISTS " + FILE_STORE_NAME + " (id unique, data TEXT);", - [], createIndex, onError); - }); - }; - WebSQL.prototype.getReadOnlyContext = function() { - return new WebSQLContext(this.db, true); - }; - WebSQL.prototype.getReadWriteContext = function() { - return new WebSQLContext(this.db, false); - }; - - module.exports = WebSQL; - -}(this)); +module.exports = WebSQL; diff --git a/tests/lib/indexeddb.js b/tests/lib/indexeddb.js index 29d7d1f..a7f0b78 100644 --- a/tests/lib/indexeddb.js +++ b/tests/lib/indexeddb.js @@ -1,56 +1,53 @@ -(function(global) { - var Filer = require("../.."); +var Filer = require("../.."); - var indexedDB = global.indexedDB || - global.mozIndexedDB || - global.webkitIndexedDB || - global.msIndexedDB; +var indexedDB = global.indexedDB || + global.mozIndexedDB || + global.webkitIndexedDB || + global.msIndexedDB; - var needsCleanup = []; - if(global.addEventListener) { - global.addEventListener('beforeunload', function() { - needsCleanup.forEach(function(f) { f(); }); - }); - } +var needsCleanup = []; +if(global.addEventListener) { + global.addEventListener('beforeunload', function() { + needsCleanup.forEach(function(f) { f(); }); + }); +} - function IndexedDBTestProvider(name) { - var _done = false; - var that = this; +function IndexedDBTestProvider(name) { + var _done = false; + var that = this; - function cleanup(callback) { - if(!that.provider || _done) { - return; - } - - // We have to force any other connections to close - // before we can delete a db. - if(that.provider.db) { - that.provider.db.close(); - } - - callback = callback || function(){}; - var request = indexedDB.deleteDatabase(name); - function finished() { - that.provider = null; - _done = true; - callback(); - } - request.onsuccess = finished; - request.onerror = finished; + function cleanup(callback) { + if(!that.provider || _done) { + return; } - function init() { - if(that.provider) { - return; - } - that.provider = new Filer.FileSystem.providers.IndexedDB(name); - needsCleanup.push(cleanup); + // We have to force any other connections to close + // before we can delete a db. + if(that.provider.db) { + that.provider.db.close(); } - this.init = init; - this.cleanup = cleanup; + callback = callback || function(){}; + var request = indexedDB.deleteDatabase(name); + function finished() { + that.provider = null; + _done = true; + callback(); + } + request.onsuccess = finished; + request.onerror = finished; } - module.exports = IndexedDBTestProvider; + function init() { + if(that.provider) { + return; + } + that.provider = new Filer.FileSystem.providers.IndexedDB(name); + needsCleanup.push(cleanup); + } -}(this)); + this.init = init; + this.cleanup = cleanup; +} + +module.exports = IndexedDBTestProvider; diff --git a/tests/lib/websql.js b/tests/lib/websql.js index c886ddf..af3bdd8 100644 --- a/tests/lib/websql.js +++ b/tests/lib/websql.js @@ -1,47 +1,43 @@ -(function(global) { +var Filer = require('../..'); - var Filer = require('../..'); +var needsCleanup = []; +if(global.addEventListener) { + window.addEventListener('beforeunload', function() { + needsCleanup.forEach(function(f) { f(); }); + }); +} - var needsCleanup = []; - if(global.addEventListener) { - window.addEventListener('beforeunload', function() { - needsCleanup.forEach(function(f) { f(); }); +function WebSQLTestProvider(name) { + var _done = false; + var that = this; + + function cleanup(callback) { + if(!that.provider || _done) { + return; + } + // Provider is there, but db was never touched + if(!that.provider.db) { + return; + } + + var context = that.provider.getReadWriteContext(); + context.clear(function() { + that.provider = null; + _done = true; + callback(); }); } - function WebSQLTestProvider(name) { - var _done = false; - var that = this; - - function cleanup(callback) { - if(!that.provider || _done) { - return; - } - // Provider is there, but db was never touched - if(!that.provider.db) { - return; - } - - var context = that.provider.getReadWriteContext(); - context.clear(function() { - that.provider = null; - _done = true; - callback(); - }); + function init() { + if(that.provider) { + return; } - - function init() { - if(that.provider) { - return; - } - that.provider = new Filer.FileSystem.providers.WebSQL(name); - needsCleanup.push(cleanup); - } - - this.init = init; - this.cleanup = cleanup; + that.provider = new Filer.FileSystem.providers.WebSQL(name); + needsCleanup.push(cleanup); } - module.exports = WebSQLTestProvider; + this.init = init; + this.cleanup = cleanup; +} -}(this)); +module.exports = WebSQLTestProvider;