diff --git a/package.json b/package.json index 50ca60a..45ac762 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,8 @@ "bower": "~1.3.8", "request": "^2.36.0", "browser-request": "git://github.com/humphd/browser-request.git#959ea95bf200d64939ed76897d3b06bb684f3a0d", - "jszip": "git://github.com/humphd/jszip.git#ad3f356bb165aba1cafeabe1bb3e49293803f975" + "jszip": "git://github.com/humphd/jszip.git#ad3f356bb165aba1cafeabe1bb3e49293803f975", + "base64-arraybuffer": "^0.1.2" }, "devDependencies": { "chai": "~1.9.1", diff --git a/src/filesystem/implementation.js b/src/filesystem/implementation.js index 9c58fa1..995deb6 100644 --- a/src/filesystem/implementation.js +++ b/src/filesystem/implementation.js @@ -37,6 +37,7 @@ var OpenFileDescription = require('../open-file-description.js'); var SuperNode = require('../super-node.js'); var Node = require('../node.js'); var Stats = require('../stats.js'); +var Buffer = require('../buffer.js'); /** * Many functions below use this callback pattern. If it's not @@ -124,7 +125,7 @@ function update_node_times(context, path, node, times, callback) { } if(update) { - context.put(node.id, node, complete); + context.putObject(node.id, node, complete); } else { complete(); } @@ -167,7 +168,7 @@ function make_node(context, path, mode, callback) { } else if(error && !(error instanceof Errors.ENOENT)) { callback(error); } else { - context.get(parentNode.data, create_node); + context.getObject(parentNode.data, create_node); } } @@ -184,7 +185,7 @@ function make_node(context, path, mode, callback) { } node = result; node.nlinks += 1; - context.put(node.id, node, update_parent_node_data); + context.putObject(node.id, node, update_parent_node_data); }); } } @@ -205,7 +206,7 @@ function make_node(context, path, mode, callback) { callback(error); } else { parentNodeData[name] = new DirectoryEntry(node.id, mode); - context.put(parentNode.data, parentNodeData, update_time); + context.putObject(parentNode.data, parentNodeData, update_time); } } @@ -233,7 +234,7 @@ function find_node(context, path, callback) { } else if(!superNode || superNode.mode !== MODE_META || !superNode.rnode) { callback(new Errors.EFILESYSTEMERROR()); } else { - context.get(superNode.rnode, check_root_directory_node); + context.getObject(superNode.rnode, check_root_directory_node); } } @@ -255,7 +256,7 @@ function find_node(context, path, callback) { } else if(parentDirectoryNode.mode !== MODE_DIRECTORY || !parentDirectoryNode.data) { callback(new Errors.ENOTDIR('a component of the path prefix is not a directory')); } else { - context.get(parentDirectoryNode.data, get_node_from_parent_directory_data); + context.getObject(parentDirectoryNode.data, get_node_from_parent_directory_data); } } @@ -269,7 +270,7 @@ function find_node(context, path, callback) { callback(new Errors.ENOENT()); } else { var nodeId = parentDirectoryData[name].id; - context.get(nodeId, is_symbolic_link); + context.getObject(nodeId, is_symbolic_link); } } } @@ -296,14 +297,14 @@ function find_node(context, path, callback) { parentPath = dirname(data); name = basename(data); if(ROOT_DIRECTORY_NAME == name) { - context.get(SUPER_NODE_ID, read_root_directory_node); + context.getObject(SUPER_NODE_ID, read_root_directory_node); } else { find_node(context, parentPath, read_parent_directory_data); } } if(ROOT_DIRECTORY_NAME == name) { - context.get(SUPER_NODE_ID, read_root_directory_node); + context.getObject(SUPER_NODE_ID, read_root_directory_node); } else { find_node(context, parentPath, read_parent_directory_data); } @@ -338,7 +339,7 @@ function set_extended_attribute (context, path_or_fd, name, value, flag, callbac } else { node.xattrs[name] = value; - context.put(node.id, node, update_time); + context.putObject(node.id, node, update_time); } } @@ -348,7 +349,7 @@ function set_extended_attribute (context, path_or_fd, name, value, flag, callbac } else if (typeof path_or_fd == 'object' && typeof path_or_fd.id == 'string') { path = path_or_fd.path; - context.get(path_or_fd.id, set_xattr); + context.getObject(path_or_fd.id, set_xattr); } else { callback(new Errors.EINVAL('path or file descriptor of wrong type')); @@ -380,7 +381,7 @@ function ensure_root_directory(context, callback) { return; } superNode = result; - context.put(superNode.id, superNode, write_directory_node); + context.putObject(superNode.id, superNode, write_directory_node); }); } } @@ -396,7 +397,7 @@ function ensure_root_directory(context, callback) { } directoryNode = result; directoryNode.nlinks += 1; - context.put(directoryNode.id, directoryNode, write_directory_data); + context.putObject(directoryNode.id, directoryNode, write_directory_data); }); } } @@ -406,11 +407,11 @@ function ensure_root_directory(context, callback) { callback(error); } else { directoryData = {}; - context.put(directoryNode.data, directoryData, callback); + context.putObject(directoryNode.data, directoryData, callback); } } - context.get(SUPER_NODE_ID, ensure_super_node); + context.getObject(SUPER_NODE_ID, ensure_super_node); } /** @@ -441,7 +442,7 @@ function make_directory(context, path, callback) { callback(error); } else { parentDirectoryNode = result; - context.get(parentDirectoryNode.data, write_directory_node); + context.getObject(parentDirectoryNode.data, write_directory_node); } } @@ -457,7 +458,7 @@ function make_directory(context, path, callback) { } directoryNode = result; directoryNode.nlinks += 1; - context.put(directoryNode.id, directoryNode, write_directory_data); + context.putObject(directoryNode.id, directoryNode, write_directory_data); }); } } @@ -467,7 +468,7 @@ function make_directory(context, path, callback) { callback(error); } else { directoryData = {}; - context.put(directoryNode.data, directoryData, update_parent_directory_data); + context.putObject(directoryNode.data, directoryData, update_parent_directory_data); } } @@ -485,7 +486,7 @@ function make_directory(context, path, callback) { callback(error); } else { parentDirectoryData[name] = new DirectoryEntry(directoryNode.id, MODE_DIRECTORY); - context.put(parentDirectoryNode.data, parentDirectoryData, update_time); + context.putObject(parentDirectoryNode.data, parentDirectoryData, update_time); } } @@ -510,7 +511,7 @@ function remove_directory(context, path, callback) { callback(error); } else { parentDirectoryNode = result; - context.get(parentDirectoryNode.data, check_if_node_exists); + context.getObject(parentDirectoryNode.data, check_if_node_exists); } } @@ -524,7 +525,7 @@ function remove_directory(context, path, callback) { } else { parentDirectoryData = result; directoryNode = parentDirectoryData[name].id; - context.get(directoryNode, check_if_node_is_directory); + context.getObject(directoryNode, check_if_node_is_directory); } } @@ -535,7 +536,7 @@ function remove_directory(context, path, callback) { callback(new Errors.ENOTDIR()); } else { directoryNode = result; - context.get(directoryNode.data, check_if_directory_is_empty); + context.getObject(directoryNode.data, check_if_directory_is_empty); } } @@ -563,7 +564,7 @@ function remove_directory(context, path, callback) { function remove_directory_entry_from_parent_directory_node() { delete parentDirectoryData[name]; - context.put(parentDirectoryNode.data, parentDirectoryData, update_time); + context.putObject(parentDirectoryNode.data, parentDirectoryData, update_time); } function remove_directory_node(error) { @@ -615,7 +616,7 @@ function open_file(context, path, flags, callback) { callback(new Errors.ENOENT()); } else { directoryNode = result; - context.get(directoryNode.data, check_if_file_exists); + context.getObject(directoryNode.data, check_if_file_exists); } } @@ -632,7 +633,7 @@ function open_file(context, path, flags, callback) { if(directoryEntry.type == MODE_DIRECTORY && _(flags).contains(O_WRITE)) { callback(new Errors.EISDIR('the named file is a directory and O_WRITE is set')); } else { - context.get(directoryEntry.id, check_if_symbolic_link); + context.getObject(directoryEntry.id, check_if_symbolic_link); } } } else { @@ -694,7 +695,7 @@ function open_file(context, path, flags, callback) { } fileNode = result; fileNode.nlinks += 1; - context.put(fileNode.id, fileNode, write_file_data); + context.putObject(fileNode.id, fileNode, write_file_data); }); } @@ -704,7 +705,7 @@ function open_file(context, path, flags, callback) { } else { fileData = new Buffer(0); fileData.fill(0); - context.put(fileNode.data, fileData, update_directory_data); + context.putBuffer(fileNode.data, fileData, update_directory_data); } } @@ -722,7 +723,7 @@ function open_file(context, path, flags, callback) { callback(error); } else { directoryData[name] = new DirectoryEntry(fileNode.id, MODE_FILE); - context.put(directoryNode.data, directoryData, update_time); + context.putObject(directoryNode.data, directoryData, update_time); } } @@ -759,7 +760,7 @@ function replace_data(context, ofd, buffer, offset, length, callback) { if(error) { callback(error); } else { - context.put(fileNode.id, fileNode, update_time); + context.putObject(fileNode.id, fileNode, update_time); } } @@ -776,11 +777,11 @@ function replace_data(context, ofd, buffer, offset, length, callback) { fileNode.size = length; fileNode.version += 1; - context.put(fileNode.data, newData, update_file_node); + context.putBuffer(fileNode.data, newData, update_file_node); } } - context.get(ofd.id, write_file_data); + context.getObject(ofd.id, write_file_data); } function write_data(context, ofd, buffer, offset, length, position, callback) { @@ -808,7 +809,7 @@ function write_data(context, ofd, buffer, offset, length, position, callback) { if(error) { callback(error); } else { - context.put(fileNode.id, fileNode, update_time); + context.putObject(fileNode.id, fileNode, update_time); } } @@ -835,7 +836,7 @@ function write_data(context, ofd, buffer, offset, length, position, callback) { fileNode.size = newSize; fileNode.version += 1; - context.put(fileNode.data, newData, update_file_node); + context.putBuffer(fileNode.data, newData, update_file_node); } } @@ -844,11 +845,11 @@ function write_data(context, ofd, buffer, offset, length, position, callback) { callback(error); } else { fileNode = result; - context.get(fileNode.data, update_file_data); + context.getBuffer(fileNode.data, update_file_data); } } - context.get(ofd.id, read_file_data); + context.getObject(ofd.id, read_file_data); } function read_data(context, ofd, buffer, offset, length, position, callback) { @@ -878,11 +879,11 @@ function read_data(context, ofd, buffer, offset, length, position, callback) { callback(error); } else { fileNode = result; - context.get(fileNode.data, handle_file_data); + context.getBuffer(fileNode.data, handle_file_data); } } - context.get(ofd.id, read_file_data); + context.getObject(ofd.id, read_file_data); } function stat_file(context, path, callback) { @@ -892,7 +893,7 @@ function stat_file(context, path, callback) { } function fstat_file(context, ofd, callback) { - context.get(ofd.id, standard_check_result_cb(callback)); + context.getObject(ofd.id, standard_check_result_cb(callback)); } function lstat_file(context, path, callback) { @@ -914,7 +915,7 @@ function lstat_file(context, path, callback) { callback(error); } else { directoryNode = result; - context.get(directoryNode.data, check_if_file_exists); + context.getObject(directoryNode.data, check_if_file_exists); } } @@ -926,7 +927,7 @@ function lstat_file(context, path, callback) { if(!_(directoryData).has(name)) { callback(new Errors.ENOENT('a component of the path does not name an existing file')); } else { - context.get(directoryData[name].id, standard_check_result_cb(callback)); + context.getObject(directoryData[name].id, standard_check_result_cb(callback)); } } } @@ -961,7 +962,7 @@ function link_node(context, oldpath, newpath, callback) { } else { fileNode = result; fileNode.nlinks += 1; - context.put(fileNode.id, fileNode, update_time); + context.putObject(fileNode.id, fileNode, update_time); } } @@ -969,7 +970,7 @@ function link_node(context, oldpath, newpath, callback) { if(error) { callback(error); } else { - context.get(newDirectoryData[newname].id, update_file_node); + context.getObject(newDirectoryData[newname].id, update_file_node); } } @@ -982,7 +983,7 @@ function link_node(context, oldpath, newpath, callback) { callback(new Errors.EEXIST('newpath resolves to an existing file')); } else { newDirectoryData[newname] = oldDirectoryData[oldname]; - context.put(newDirectoryNode.data, newDirectoryData, read_directory_entry); + context.putObject(newDirectoryNode.data, newDirectoryData, read_directory_entry); } } } @@ -992,7 +993,7 @@ function link_node(context, oldpath, newpath, callback) { callback(error); } else { newDirectoryNode = result; - context.get(newDirectoryNode.data, check_if_new_file_exists); + context.getObject(newDirectoryNode.data, check_if_new_file_exists); } } @@ -1014,7 +1015,7 @@ function link_node(context, oldpath, newpath, callback) { callback(error); } else { oldDirectoryNode = result; - context.get(oldDirectoryNode.data, check_if_old_file_exists); + context.getObject(oldDirectoryNode.data, check_if_old_file_exists); } } @@ -1035,7 +1036,7 @@ function unlink_node(context, path, callback) { callback(error); } else { delete directoryData[name]; - context.put(directoryNode.data, directoryData, function(error) { + context.putObject(directoryNode.data, directoryData, function(error) { var now = Date.now(); update_node_times(context, parentPath, directoryNode, { mtime: now, ctime: now }, callback); }); @@ -1059,7 +1060,7 @@ function unlink_node(context, path, callback) { if(fileNode.nlinks < 1) { context.delete(fileNode.id, delete_file_data); } else { - context.put(fileNode.id, fileNode, function(error) { + context.putObject(fileNode.id, fileNode, function(error) { update_node_times(context, path, fileNode, { ctime: Date.now() }, update_directory_data); }); } @@ -1074,7 +1075,7 @@ function unlink_node(context, path, callback) { if(!_(directoryData).has(name)) { callback(new Errors.ENOENT('a component of the path does not name an existing file')); } else { - context.get(directoryData[name].id, update_file_node); + context.getObject(directoryData[name].id, update_file_node); } } } @@ -1084,7 +1085,7 @@ function unlink_node(context, path, callback) { callback(error); } else { directoryNode = result; - context.get(directoryNode.data, check_if_file_exists); + context.getObject(directoryNode.data, check_if_file_exists); } } @@ -1113,7 +1114,7 @@ function read_directory(context, path, callback) { callback(error); } else { directoryNode = result; - context.get(directoryNode.data, handle_directory_data); + context.getObject(directoryNode.data, handle_directory_data); } } @@ -1140,7 +1141,7 @@ function make_symbolic_link(context, srcpath, dstpath, callback) { callback(error); } else { directoryNode = result; - context.get(directoryNode.data, check_if_file_exists); + context.getObject(directoryNode.data, check_if_file_exists); } } @@ -1167,7 +1168,7 @@ function make_symbolic_link(context, srcpath, dstpath, callback) { fileNode.nlinks += 1; fileNode.size = srcpath.length; fileNode.data = srcpath; - context.put(fileNode.id, fileNode, update_directory_data); + context.putObject(fileNode.id, fileNode, update_directory_data); }); } @@ -1185,7 +1186,7 @@ function make_symbolic_link(context, srcpath, dstpath, callback) { callback(error); } else { directoryData[name] = new DirectoryEntry(fileNode.id, MODE_SYMBOLIC_LINK); - context.put(directoryNode.data, directoryData, update_time); + context.putObject(directoryNode.data, directoryData, update_time); } } } @@ -1205,7 +1206,7 @@ function read_link(context, path, callback) { callback(error); } else { directoryNode = result; - context.get(directoryNode.data, check_if_file_exists); + context.getObject(directoryNode.data, check_if_file_exists); } } @@ -1217,7 +1218,7 @@ function read_link(context, path, callback) { if(!_(directoryData).has(name)) { callback(new Errors.ENOENT('a component of the path does not name an existing file')); } else { - context.get(directoryData[name].id, check_if_symbolic); + context.getObject(directoryData[name].id, check_if_symbolic); } } } @@ -1247,7 +1248,7 @@ function truncate_file(context, path, length, callback) { callback(new Errors.EISDIR()); } else{ fileNode = node; - context.get(fileNode.data, truncate_file_data); + context.getObject(fileNode.data, truncate_file_data); } } @@ -1264,7 +1265,7 @@ function truncate_file(context, path, length, callback) { if(fileData) { fileData.copy(data); } - context.put(fileNode.data, data, update_file_node); + context.putBuffer(fileNode.data, data, update_file_node); } } @@ -1283,7 +1284,7 @@ function truncate_file(context, path, length, callback) { } else { fileNode.size = length; fileNode.version += 1; - context.put(fileNode.id, fileNode, update_time); + context.putObject(fileNode.id, fileNode, update_time); } } @@ -1304,7 +1305,7 @@ function ftruncate_file(context, ofd, length, callback) { callback(new Errors.EISDIR()); } else{ fileNode = node; - context.get(fileNode.data, truncate_file_data); + context.getObject(fileNode.data, truncate_file_data); } } @@ -1323,7 +1324,7 @@ function ftruncate_file(context, ofd, length, callback) { data = new Buffer(length); data.fill(0); } - context.put(fileNode.data, data, update_file_node); + context.putBuffer(fileNode.data, data, update_file_node); } } @@ -1342,14 +1343,14 @@ function ftruncate_file(context, ofd, length, callback) { } else { fileNode.size = length; fileNode.version += 1; - context.put(fileNode.id, fileNode, update_time); + context.putObject(fileNode.id, fileNode, update_time); } } if(length < 0) { callback(new Errors.EINVAL('length cannot be negative')); } else { - context.get(ofd.id, read_file_data); + context.getObject(ofd.id, read_file_data); } } @@ -1392,7 +1393,7 @@ function futimes_file(context, ofd, atime, mtime, callback) { callback(new Errors.EINVAL('atime and mtime must be positive integers')); } else { - context.get(ofd.id, update_times); + context.getObject(ofd.id, update_times); } } @@ -1481,7 +1482,7 @@ function fgetxattr_file (context, ofd, name, callback) { callback(new Errors.EINVAL('attribute name cannot be an empty string')); } else { - context.get(ofd.id, get_xattr); + context.getObject(ofd.id, get_xattr); } } @@ -1507,7 +1508,7 @@ function removexattr_file (context, path, name, callback) { } else { delete node.xattrs[name]; - context.put(node.id, node, update_time); + context.putObject(node.id, node, update_time); } } @@ -1541,7 +1542,7 @@ function fremovexattr_file (context, ofd, name, callback) { } else { delete node.xattrs[name]; - context.put(node.id, node, update_time); + context.putObject(node.id, node, update_time); } } @@ -1552,7 +1553,7 @@ function fremovexattr_file (context, ofd, name, callback) { callback(new Errors.EINVAL('attribute name cannot be an empty string')); } else { - context.get(ofd.id, remove_xattr); + context.getObject(ofd.id, remove_xattr); } } diff --git a/src/filesystem/interface.js b/src/filesystem/interface.js index 38f31a8..b894afd 100644 --- a/src/filesystem/interface.js +++ b/src/filesystem/interface.js @@ -156,7 +156,7 @@ function FileSystem(options, callback) { // Otherwise (default) make sure this id is unused first function guidWithCheck(callback) { var id = guid(); - context.get(id, function(err, value) { + context.getObject(id, function(err, value) { if(err) { callback(err); return; diff --git a/src/providers/indexeddb.js b/src/providers/indexeddb.js index 1b5d8ca..557050a 100644 --- a/src/providers/indexeddb.js +++ b/src/providers/indexeddb.js @@ -3,6 +3,7 @@ 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 FilerBuffer = require('../buffer.js'); var indexedDB = global.indexedDB || global.mozIndexedDB || @@ -13,6 +14,7 @@ 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(); @@ -26,9 +28,10 @@ IndexedDBContext.prototype.clear = function(callback) { callback(e); } }; -IndexedDBContext.prototype.get = function(key, callback) { + +function _get(objectStore, key, callback) { try { - var request = this.objectStore.get(key); + var request = objectStore.get(key); request.onsuccess = function onsuccess(event) { var result = event.target.result; callback(null, result); @@ -39,10 +42,22 @@ IndexedDBContext.prototype.get = function(key, callback) { } catch(e) { callback(e); } +} +IndexedDBContext.prototype.getObject = function(key, callback) { + _get(this.objectStore, key, callback); }; -IndexedDBContext.prototype.put = function(key, value, callback) { +IndexedDBContext.prototype.getBuffer = function(key, callback) { + _get(this.objectStore, key, function(err, arrayBuffer) { + if(err) { + return callback(err); + } + callback(null, new FilerBuffer(arrayBuffer)); + }); +}; + +function _put(objectStore, key, value, callback) { try { - var request = this.objectStore.put(value, key); + var request = objectStore.put(value, key); request.onsuccess = function onsuccess(event) { var result = event.target.result; callback(null, result); @@ -53,7 +68,14 @@ IndexedDBContext.prototype.put = function(key, value, callback) { } catch(e) { callback(e); } +} +IndexedDBContext.prototype.putObject = function(key, value, callback) { + _put(this.objectStore, key, value, callback); }; +IndexedDBContext.prototype.putBuffer = function(key, uint8BackedBuffer, callback) { + _put(this.objectStore, key, uint8BackedBuffer.buffer, callback); +}; + IndexedDBContext.prototype.delete = function(key, callback) { try { var request = this.objectStore.delete(key); diff --git a/src/providers/memory.js b/src/providers/memory.js index 32a229d..0fb17b7 100644 --- a/src/providers/memory.js +++ b/src/providers/memory.js @@ -24,6 +24,7 @@ function MemoryContext(db, readOnly) { this.readOnly = readOnly; this.objectStore = db; } + MemoryContext.prototype.clear = function(callback) { if(this.readOnly) { asyncCallback(function() { @@ -37,13 +38,19 @@ MemoryContext.prototype.clear = function(callback) { }); asyncCallback(callback); }; -MemoryContext.prototype.get = function(key, callback) { + +// Memory context doesn't care about differences between Object and Buffer +MemoryContext.prototype.getObject = +MemoryContext.prototype.getBuffer = +function(key, callback) { var that = this; asyncCallback(function() { callback(null, that.objectStore[key]); }); }; -MemoryContext.prototype.put = function(key, value, callback) { +MemoryContext.prototype.putObject = +MemoryContext.prototype.putBuffer = +function(key, value, callback) { if(this.readOnly) { asyncCallback(function() { callback("[MemoryContext] Error: write operation on read only context"); @@ -53,6 +60,7 @@ MemoryContext.prototype.put = function(key, value, callback) { this.objectStore[key] = value; asyncCallback(callback); }; + MemoryContext.prototype.delete = function(key, callback) { if(this.readOnly) { asyncCallback(function() { diff --git a/src/providers/websql.js b/src/providers/websql.js index ba09e13..c6ed66a 100644 --- a/src/providers/websql.js +++ b/src/providers/websql.js @@ -3,8 +3,9 @@ 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 FilerBuffer = require('../buffer.js'); +var base64ArrayBuffer = require('base64-arraybuffer'); function WebSQLContext(db, isReadOnly) { var that = this; @@ -20,6 +21,7 @@ function WebSQLContext(db, isReadOnly) { }); }; } + WebSQLContext.prototype.clear = function(callback) { function onError(transaction, error) { callback(error); @@ -32,52 +34,74 @@ WebSQLContext.prototype.clear = function(callback) { [], onSuccess, onError); }); }; -WebSQLContext.prototype.get = function(key, callback) { + +function _get(getTransaction, 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); - } + callback(null, value); } function onError(transaction, error) { callback(error); } - this.getTransaction(function(transaction) { - transaction.executeSql("SELECT data FROM " + FILE_STORE_NAME + " WHERE id = ?;", + getTransaction(function(transaction) { + transaction.executeSql("SELECT data FROM " + FILE_STORE_NAME + " WHERE id = ? LIMIT 1;", [key], onSuccess, onError); }); +} +WebSQLContext.prototype.getObject = function(key, callback) { + _get(this.getTransaction, key, function(err, result) { + if(err) { + return callback(err); + } + + try { + if(result) { + result = JSON.parse(result); + } + } catch(e) { + return callback(e); + } + + callback(null, result); + }); }; -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); +WebSQLContext.prototype.getBuffer = function(key, callback) { + _get(this.getTransaction, key, function(err, result) { + if(err) { + return callback(err); + } + + if(result) { + var arrayBuffer = base64ArrayBuffer.decode(result); + result = new FilerBuffer(arrayBuffer); + } + + callback(null, result); + }); +}; + +function _put(getTransaction, key, value, callback) { function onSuccess(transaction, result) { callback(null); } function onError(transaction, error) { callback(error); } - this.getTransaction(function(transaction) { + getTransaction(function(transaction) { transaction.executeSql("INSERT OR REPLACE INTO " + FILE_STORE_NAME + " (id, data) VALUES (?, ?);", [key, value], onSuccess, onError); }); +} +WebSQLContext.prototype.putObject = function(key, value, callback) { + var json = JSON.stringify(value); + _put(this.getTransaction, key, json, callback); }; +WebSQLContext.prototype.putBuffer = function(key, uint8BackedBuffer, callback) { + var base64 = base64ArrayBuffer.encode(uint8BackedBuffer.buffer); + _put(this.getTransaction, key, base64, callback); +}; + WebSQLContext.prototype.delete = function(key, callback) { function onSuccess(transaction, result) { callback(null); diff --git a/tests/lib/indexeddb.js b/tests/lib/indexeddb.js index a7f0b78..251729f 100644 --- a/tests/lib/indexeddb.js +++ b/tests/lib/indexeddb.js @@ -18,7 +18,7 @@ function IndexedDBTestProvider(name) { function cleanup(callback) { if(!that.provider || _done) { - return; + return callback(); } // We have to force any other connections to close @@ -49,5 +49,8 @@ function IndexedDBTestProvider(name) { this.init = init; this.cleanup = cleanup; } +IndexedDBTestProvider.isSupported = function() { + return Filer.FileSystem.providers.IndexedDB.isSupported(); +}; module.exports = IndexedDBTestProvider; diff --git a/tests/lib/memory.js b/tests/lib/memory.js index fff7bbd..19d25b6 100644 --- a/tests/lib/memory.js +++ b/tests/lib/memory.js @@ -18,5 +18,8 @@ function MemoryTestProvider(name) { this.init = init; this.cleanup = cleanup; } +MemoryTestProvider.isSupported = function() { + return Filer.FileSystem.providers.Memory.isSupported(); +}; module.exports = MemoryTestProvider; diff --git a/tests/lib/websql.js b/tests/lib/websql.js index 9a02dcd..9bc7c93 100644 --- a/tests/lib/websql.js +++ b/tests/lib/websql.js @@ -13,11 +13,11 @@ function WebSQLTestProvider(name) { function cleanup(callback) { if(!that.provider || _done) { - return; + return callback(); } // Provider is there, but db was never touched if(!that.provider.db) { - return; + return callback(); } var context = that.provider.getReadWriteContext(); @@ -39,5 +39,8 @@ function WebSQLTestProvider(name) { this.init = init; this.cleanup = cleanup; } +WebSQLTestProvider.isSupported = function() { + return Filer.FileSystem.providers.WebSQL.isSupported(); +}; module.exports = WebSQLTestProvider; diff --git a/tests/spec/providers/providers.base.js b/tests/spec/providers/providers.base.js new file mode 100644 index 0000000..e133d5d --- /dev/null +++ b/tests/spec/providers/providers.base.js @@ -0,0 +1,146 @@ +var Buffer = require('../../..').Buffer; +var util = require('../../lib/test-utils.js'); +var expect = require('chai').expect; + +/** + * Due to the different setup/cleanup needs of the built-in providers, + * we use the test provider wrappers instead of the raw providers themselves. + */ +module.exports = function createProviderTestsFor(providerName, testProvider) { + if(!testProvider.isSupported()) { + console.log("Skipping provider tests for `" + providerName +"'--not supported in current environment."); + return; + } + + describe("Filer Provider Tests for " + providerName, function() { + var _provider; + var provider; + + beforeEach(function() { + _provider = new testProvider(util.uniqueName()); + _provider.init(); + provider = _provider.provider; + }); + + afterEach(function(done) { + _provider.cleanup(done); + _provider = null; + provider = null; + }); + + + it("has open, getReadOnlyContext, and getReadWriteContext instance methods", function() { + expect(provider.open).to.be.a('function'); + expect(provider.getReadOnlyContext).to.be.a('function'); + expect(provider.getReadWriteContext).to.be.a('function'); + }); + + it("should open a new IndexedDB database", function(done) { + provider.open(function(error, firstAccess) { + expect(error).not.to.exist; + expect(firstAccess).to.be.true; + done(); + }); + }); + + it("should allow putObject() and getObject()", function(done) { + provider.open(function(error, firstAccess) { + if(error) throw error; + + var context = provider.getReadWriteContext(); + // Simple JS Object + var value = { + a: "a", + b: 1, + c: true, + d: [1,2,3], + e: { + e1: ['a', 'b', 'c'] + } + }; + context.putObject("key", value, function(error) { + if(error) throw error; + + context.getObject("key", function(error, result) { + expect(error).not.to.exist; + expect(result).to.be.an('object'); + expect(result).to.deep.equal(value); + done(); + }); + }); + }); + }); + + it("should allow putBuffer() and getBuffer()", function(done) { + provider.open(function(error, firstAccess) { + if(error) throw error; + + var context = provider.getReadWriteContext(); + // Filer Buffer + var buf = new Buffer([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + context.putBuffer("key", buf, function(error) { + if(error) throw error; + + context.getBuffer("key", function(error, result) { + expect(error).not.to.exist; + expect(Buffer.isBuffer(result)).to.be.true; + expect(result).to.deep.equal(buf); + done(); + }); + }); + }); + }); + + it("should allow delete()", function(done) { + var provider = _provider.provider; + provider.open(function(error, firstAccess) { + if(error) throw error; + + var context = provider.getReadWriteContext(); + context.putObject("key", "value", function(error) { + if(error) throw error; + + context.delete("key", function(error) { + if(error) throw error; + + context.getObject("key", function(error, result) { + expect(error).not.to.exist; + expect(result).not.to.exist; + done(); + }); + }); + }); + }); + }); + + it("should allow clear()", function(done) { + provider.open(function(error, firstAccess) { + if(error) throw error; + + var context = provider.getReadWriteContext(); + context.putObject("key1", "value1", function(error) { + if(error) throw error; + + context.putObject("key2", "value2", function(error) { + if(error) throw error; + + context.clear(function(err) { + if(error) throw error; + + context.getObject("key1", function(error, result) { + if(error) throw error; + expect(result).not.to.exist; + + context.getObject("key2", function(error, result) { + if(error) throw error; + expect(result).not.to.exist; + done(); + }); + }); + }); + }); + }); + }); + }); + }); +}; diff --git a/tests/spec/providers/providers.indexeddb.spec.js b/tests/spec/providers/providers.indexeddb.spec.js index 6ea4662..1fa6b29 100644 --- a/tests/spec/providers/providers.indexeddb.spec.js +++ b/tests/spec/providers/providers.indexeddb.spec.js @@ -1,148 +1,4 @@ -var Filer = require('../../..'); var util = require('../../lib/test-utils.js'); -var expect = require('chai').expect; +var providerBase = require('./providers.base.js'); -if(!Filer.FileSystem.providers.IndexedDB.isSupported()) { - console.log("Skipping Filer.FileSystem.providers.IndexedDB tests, since IndexedDB isn't supported."); -} else { - describe("Filer.FileSystem.providers.IndexedDB", function() { - it("is supported -- if it isn't, none of these tests can run.", function() { - expect(Filer.FileSystem.providers.IndexedDB.isSupported()).to.be.true; - }); - - it("has open, getReadOnlyContext, and getReadWriteContext instance methods", function() { - var indexedDBProvider = new Filer.FileSystem.providers.IndexedDB(); - expect(indexedDBProvider.open).to.be.a('function'); - expect(indexedDBProvider.getReadOnlyContext).to.be.a('function'); - expect(indexedDBProvider.getReadWriteContext).to.be.a('function'); - }); - - describe("open an IndexedDB provider", function() { - var _provider; - - beforeEach(function() { - _provider = new util.providers.IndexedDB(util.uniqueName()); - _provider.init(); - }); - - afterEach(function(done) { - _provider.cleanup(done); - _provider = null; - }); - - it("should open a new IndexedDB database", function(done) { - var provider = _provider.provider; - provider.open(function(error, firstAccess) { - expect(error).not.to.exist; - expect(firstAccess).to.be.true; - done(); - }); - }); - }); - - describe("Read/Write operations on an IndexedDB provider", function() { - var _provider; - - beforeEach(function() { - _provider = new util.providers.IndexedDB(util.uniqueName()); - _provider.init(); - }); - - afterEach(function(done) { - _provider.cleanup(done); - _provider = null; - }); - - it("should allow put() and get()", function(done) { - var provider = _provider.provider; - provider.open(function(error, firstAccess) { - if(error) throw error; - - var context = provider.getReadWriteContext(); - context.put("key", "value", function(error, result) { - if(error) throw error; - - context.get("key", function(error, result) { - expect(error).not.to.exist; - expect(result).to.equal('value'); - done(); - }); - }); - }); - }); - - it("should allow delete()", function(done) { - var provider = _provider.provider; - provider.open(function(error, firstAccess) { - if(error) throw error; - - var context = provider.getReadWriteContext(); - context.put("key", "value", function(error, result) { - if(error) throw error; - - context.delete("key", function(error, result) { - if(error) throw error; - - context.get("key", function(error, result) { - expect(error).not.to.exist; - expect(result).not.to.exist; - done(); - }); - }); - }); - }); - }); - - it("should allow clear()", function(done) { - var provider = _provider.provider; - provider.open(function(error, firstAccess) { - if(error) throw error; - - var context = provider.getReadWriteContext(); - context.put("key1", "value1", function(error, result) { - if(error) throw error; - - context.put("key2", "value2", function(error, result) { - if(error) throw error; - - context.clear(function(err) { - if(error) throw error; - - context.get("key1", function(error, result) { - if(error) throw error; - expect(result).not.to.exist; - - context.get("key2", function(error, result) { - if(error) throw error; - expect(result).not.to.exist; - done(); - }); - }); - }); - }); - }); - }); - }); - - /** - * With issue 123 (see https://github.com/js-platform/filer/issues/128) we had to - * start using readwrite contexts everywhere with IndexedDB. Skipping for now. - */ - it.skip("should fail when trying to write on ReadOnlyContext", function(done) { - var provider = _provider.provider; - provider.open(function(error, firstAccess) { - if(error) throw error; - - var context = provider.getReadOnlyContext(); - context.put("key1", "value1", function(error, result) { - expect(error).to.exist; - expect(result).not.to.exist; - done(); - }); - }); - }); - }); - - }); - -} +providerBase('IndexedDB', util.providers.IndexedDB); diff --git a/tests/spec/providers/providers.memory.spec.js b/tests/spec/providers/providers.memory.spec.js index 482075a..57849aa 100644 --- a/tests/spec/providers/providers.memory.spec.js +++ b/tests/spec/providers/providers.memory.spec.js @@ -1,145 +1,4 @@ -var Filer = require('../../..'); -var expect = require('chai').expect; +var util = require('../../lib/test-utils.js'); +var providerBase = require('./providers.base.js'); -describe("Filer.FileSystem.providers.Memory", function() { - it("is supported -- if it isn't, none of these tests can run.", function() { - expect(Filer.FileSystem.providers.Memory.isSupported()).to.be.true; - }); - - it("has open, getReadOnlyContext, and getReadWriteContext instance methods", function() { - var memoryProvider = new Filer.FileSystem.providers.Memory(); - expect(memoryProvider.open).to.be.a('function'); - expect(memoryProvider.getReadOnlyContext).to.be.a('function'); - expect(memoryProvider.getReadWriteContext).to.be.a('function'); - }); - - describe("Memory provider DBs are sharable", function() { - it("should share a single memory db when name is the same", function(done) { - var provider1; - var provider2; - var provider3; - var name1 = 'memory-db'; - var name2 = 'memory-db2'; - - provider1 = new Filer.FileSystem.providers.Memory(name1); - provider1.open(function(error, firstAccess) { - expect(error).not.to.exist; - expect(firstAccess).to.be.true; - - provider2 = new Filer.FileSystem.providers.Memory(name1); - provider2.open(function(error, firstAccess) { - expect(error).not.to.exist; - expect(firstAccess).to.be.false; - expect(provider1.db).to.equal(provider2.db); - - provider3 = new Filer.FileSystem.providers.Memory(name2); - provider3.open(function(error, firstAccess) { - expect(error).not.to.exist; - expect(firstAccess).to.be.true; - expect(provider3.db).not.to.equal(provider2.db); - - done(); - }); - }); - }); - }); - }); - - describe("open an Memory provider", function() { - it("should open a new Memory database", function(done) { - var provider = new Filer.FileSystem.providers.Memory(); - provider.open(function(error, firstAccess) { - expect(error).not.to.exist; - expect(firstAccess).to.be.true; - done(); - }); - }); - }); - - describe("Read/Write operations on an Memory provider", function() { - it("should allow put() and get()", function(done) { - var provider = new Filer.FileSystem.providers.Memory(); - provider.open(function(error, firstAccess) { - if(error) throw error; - - var context = provider.getReadWriteContext(); - context.put("key", "value", function(error, result) { - if(error) throw error; - - context.get("key", function(error, result) { - expect(error).not.to.exist; - expect(result).to.equal("value"); - done(); - }); - }); - }); - }); - - it("should allow delete()", function(done) { - var provider = new Filer.FileSystem.providers.Memory(); - provider.open(function(error, firstAccess) { - if(error) throw error; - - var context = provider.getReadWriteContext(); - context.put("key", "value", function(error, result) { - if(error) throw error; - - context.delete("key", function(error, result) { - if(error) throw error; - - context.get("key", function(error, result) { - expect(error).not.to.exist; - expect(result).not.to.exist; - done(); - }); - }); - }); - }); - }); - - it("should allow clear()", function(done) { - var provider = new Filer.FileSystem.providers.Memory(); - provider.open(function(error, firstAccess) { - if(error) throw error; - - var context = provider.getReadWriteContext(); - context.put("key1", "value1", function(error, result) { - if(error) throw error; - - context.put("key2", "value2", function(error, result) { - if(error) throw error; - - context.clear(function(err) { - if(error) throw error; - - context.get("key1", function(error, result) { - if(error) throw error; - expect(result).not.to.exist; - - context.get("key2", function(error, result) { - if(error) throw error; - expect(result).not.to.exist; - done(); - }); - }); - }); - }); - }); - }); - }); - - it("should fail when trying to write on ReadOnlyContext", function(done) { - var provider = new Filer.FileSystem.providers.Memory(); - provider.open(function(error, firstAccess) { - if(error) throw error; - - var context = provider.getReadOnlyContext(); - context.put("key1", "value1", function(error, result) { - expect(error).to.exist; - expect(result).not.to.exist; - done(); - }); - }); - }); - }); -}); +providerBase('Memory', util.providers.Memory); diff --git a/tests/spec/providers/providers.websql.spec.js b/tests/spec/providers/providers.websql.spec.js index 190caac..a1324f4 100644 --- a/tests/spec/providers/providers.websql.spec.js +++ b/tests/spec/providers/providers.websql.spec.js @@ -1,143 +1,4 @@ -var Filer = require('../../..'); var util = require('../../lib/test-utils.js'); -var expect = require('chai').expect; +var providerBase = require('./providers.base.js'); -if(!Filer.FileSystem.providers.WebSQL.isSupported()) { - console.log("Skipping Filer.FileSystem.providers.WebSQL tests, since WebSQL isn't supported."); -} else { - describe("Filer.FileSystem.providers.WebSQL", function() { - it("is supported -- if it isn't, none of these tests can run.", function() { - expect(Filer.FileSystem.providers.WebSQL.isSupported()).to.be.true; - }); - - it("has open, getReadOnlyContext, and getReadWriteContext instance methods", function() { - var webSQLProvider = new Filer.FileSystem.providers.WebSQL(); - expect(webSQLProvider.open).to.be.a('function'); - expect(webSQLProvider.getReadOnlyContext).to.be.a('function'); - expect(webSQLProvider.getReadWriteContext).to.be.a('function'); - }); - - describe("open an WebSQL provider", function() { - var _provider; - - beforeEach(function() { - _provider = new util.providers.WebSQL(util.uniqueName()); - _provider.init(); - }); - - afterEach(function(done) { - _provider.cleanup(done); - _provider = null; - }); - - it("should open a new WebSQL database", function(done) { - var provider = _provider.provider; - provider.open(function(error, firstAccess) { - expect(error).not.to.exist; - expect(firstAccess).to.be.true; - done(); - }); - }); - }); - - describe("Read/Write operations on an WebSQL provider", function() { - var _provider; - - beforeEach(function() { - _provider = new util.providers.WebSQL(util.uniqueName()); - _provider.init(); - }); - - afterEach(function(done) { - _provider.cleanup(done); - _provider = null; - }); - - it("should allow put() and get()", function(done) { - var provider = _provider.provider; - provider.open(function(error, firstAccess) { - if(error) throw error; - - var context = provider.getReadWriteContext(); - context.put("key", "value", function(error, result) { - if(error) throw error; - - context.get("key", function(error, result) { - expect(error).not.to.exist; - expect(result).to.equal("value"); - done(); - }); - }); - }); - }); - - it("should allow delete()", function(done) { - var provider = _provider.provider; - provider.open(function(error, firstAccess) { - if(error) throw error; - - var context = provider.getReadWriteContext(); - context.put("key", "value", function(error, result) { - if(error) throw error; - - context.delete("key", function(error, result) { - if(error) throw error; - - context.get("key", function(error, result) { - expect(error).not.to.exist; - expect(result).not.to.exist; - done(); - }); - }); - }); - }); - }); - - it("should allow clear()", function(done) { - var provider = _provider.provider; - provider.open(function(error, firstAccess) { - if(error) throw error; - - var context = provider.getReadWriteContext(); - context.put("key1", "value1", function(error, result) { - if(error) throw error; - - context.put("key2", "value2", function(error, result) { - if(error) throw error; - - context.clear(function(err) { - if(error) throw error; - - context.get("key1", function(error, result) { - if(error) throw error; - expect(result).not.to.exist; - - context.get("key2", function(error, result) { - expect(error).not.to.exist; - expect(result).not.to.exist; - done(); - }); - }); - }); - }); - }); - }); - }); - - it("should fail when trying to write on ReadOnlyContext", function(done) { - var provider = _provider.provider; - provider.open(function(error, firstAccess) { - if(error) throw error; - - var context = provider.getReadOnlyContext(); - context.put("key1", "value1", function(error, result) { - expect(error).to.exist; - expect(result).not.to.exist; - done(); - }); - }); - }); - }); - }); - -} +providerBase('WebSQL', util.providers.WebSQL); diff --git a/tests/spec/times.spec.js b/tests/spec/times.spec.js index f64cf7f..b6ff1c5 100644 --- a/tests/spec/times.spec.js +++ b/tests/spec/times.spec.js @@ -358,12 +358,12 @@ describe('node times (atime, mtime, ctime)', function() { var buffer = new Filer.Buffer([1, 2, 3, 4, 5, 6, 7, 8]); createTree(function() { - fs.open('/myfile', 'w', function(err, fd) { - if(err) throw error; + fs.open('/myfile', 'w', function(error, fd) { + if(error) throw error; stat('/myfile', function(stats1) { - fs.write(fd, buffer, 0, buffer.length, 0, function(err, nbytes) { - if(err) throw error; + fs.write(fd, buffer, 0, buffer.length, 0, function(error, nbytes) { + if(error) throw error; fs.close(fd, function(error) { if(error) throw error;