2013-10-02 10:27:07 -07:00
|
|
|
/* -*- Mode: js2; js2-basic-offset: 2; indent-tabs-mode: nil; -*- */
|
|
|
|
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
'use strict'
|
|
|
|
|
2013-10-02 10:27:15 -07:00
|
|
|
this.EXPORTED_SYMBOLS = ["DataStore", "DataStoreAccess"];
|
2013-10-02 10:27:07 -07:00
|
|
|
|
2013-10-02 10:27:09 -07:00
|
|
|
function debug(s) {
|
|
|
|
// dump('DEBUG DataStore: ' + s + '\n');
|
|
|
|
}
|
|
|
|
|
|
|
|
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
|
|
|
|
2013-10-02 10:27:11 -07:00
|
|
|
const REVISION_ADDED = "added";
|
|
|
|
const REVISION_UPDATED = "updated";
|
|
|
|
const REVISION_REMOVED = "removed";
|
|
|
|
const REVISION_VOID = "void";
|
|
|
|
|
2013-10-02 10:27:09 -07:00
|
|
|
Cu.import("resource://gre/modules/DataStoreDB.jsm");
|
|
|
|
Cu.import("resource://gre/modules/ObjectWrapper.jsm");
|
|
|
|
Cu.import('resource://gre/modules/Services.jsm');
|
2013-10-02 10:27:15 -07:00
|
|
|
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
|
|
|
|
|
|
|
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
|
|
|
"@mozilla.org/childprocessmessagemanager;1",
|
|
|
|
"nsIMessageSender");
|
2013-10-02 10:27:09 -07:00
|
|
|
|
2013-10-02 10:27:11 -07:00
|
|
|
/* Helper function */
|
|
|
|
function createDOMError(aWindow, aEvent) {
|
|
|
|
return new aWindow.DOMError(aEvent.target.error.name);
|
|
|
|
}
|
|
|
|
|
2013-10-02 10:27:07 -07:00
|
|
|
/* DataStore object */
|
|
|
|
|
2013-10-02 10:27:09 -07:00
|
|
|
function DataStore(aAppId, aName, aOwner, aReadOnly, aGlobalScope) {
|
2013-10-02 10:27:07 -07:00
|
|
|
this.appId = aAppId;
|
|
|
|
this.name = aName;
|
|
|
|
this.owner = aOwner;
|
|
|
|
this.readOnly = aReadOnly;
|
2013-10-02 10:27:09 -07:00
|
|
|
|
|
|
|
this.db = new DataStoreDB();
|
|
|
|
this.db.init(aOwner, aName, aGlobalScope);
|
2013-10-02 10:27:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
DataStore.prototype = {
|
|
|
|
appId: null,
|
|
|
|
name: null,
|
|
|
|
owner: null,
|
|
|
|
readOnly: null,
|
2013-10-02 10:27:11 -07:00
|
|
|
revisionId: null,
|
2013-10-02 10:27:07 -07:00
|
|
|
|
2013-10-02 10:27:09 -07:00
|
|
|
newDBPromise: function(aWindow, aTxnType, aFunction) {
|
|
|
|
let db = this.db;
|
|
|
|
return new aWindow.Promise(function(aResolve, aReject) {
|
|
|
|
debug("DBPromise started");
|
|
|
|
db.txn(
|
|
|
|
aTxnType,
|
2013-10-02 10:27:11 -07:00
|
|
|
function(aTxn, aStore, aRevisionStore) {
|
2013-10-02 10:27:09 -07:00
|
|
|
debug("DBPromise success");
|
2013-10-02 10:27:11 -07:00
|
|
|
aFunction(aResolve, aReject, aTxn, aStore, aRevisionStore);
|
2013-10-02 10:27:09 -07:00
|
|
|
},
|
2013-10-02 10:27:11 -07:00
|
|
|
function(aEvent) {
|
2013-10-02 10:27:09 -07:00
|
|
|
debug("DBPromise error");
|
2013-10-02 10:27:11 -07:00
|
|
|
aReject(createDOMError(aWindow, aEvent));
|
2013-10-02 10:27:09 -07:00
|
|
|
}
|
|
|
|
);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2013-10-02 10:27:11 -07:00
|
|
|
getInternal: function(aWindow, aResolve, aStore, aId) {
|
2013-10-02 10:27:09 -07:00
|
|
|
debug("GetInternal " + aId);
|
|
|
|
|
|
|
|
let request = aStore.get(aId);
|
|
|
|
request.onsuccess = function(aEvent) {
|
|
|
|
debug("GetInternal success. Record: " + aEvent.target.result);
|
|
|
|
aResolve(ObjectWrapper.wrap(aEvent.target.result, aWindow));
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
2013-10-02 10:27:11 -07:00
|
|
|
updateInternal: function(aResolve, aStore, aRevisionStore, aId, aObj) {
|
2013-10-02 10:27:09 -07:00
|
|
|
debug("UpdateInternal " + aId);
|
|
|
|
|
2013-10-02 10:27:11 -07:00
|
|
|
let self = this;
|
2013-10-02 10:27:09 -07:00
|
|
|
let request = aStore.put(aObj, aId);
|
|
|
|
request.onsuccess = function(aEvent) {
|
|
|
|
debug("UpdateInternal success");
|
2013-10-02 10:27:11 -07:00
|
|
|
|
|
|
|
self.addRevision(aRevisionStore, aId, REVISION_UPDATED,
|
|
|
|
function() {
|
|
|
|
debug("UpdateInternal - revisionId increased");
|
|
|
|
// No wrap here because the result is always a int.
|
|
|
|
aResolve(aEvent.target.result);
|
|
|
|
}
|
|
|
|
);
|
2013-10-02 10:27:09 -07:00
|
|
|
};
|
|
|
|
},
|
|
|
|
|
2013-10-02 10:27:11 -07:00
|
|
|
addInternal: function(aResolve, aStore, aRevisionStore, aObj) {
|
2013-10-02 10:27:09 -07:00
|
|
|
debug("AddInternal");
|
|
|
|
|
2013-10-02 10:27:11 -07:00
|
|
|
let self = this;
|
2013-10-02 10:27:09 -07:00
|
|
|
let request = aStore.put(aObj);
|
|
|
|
request.onsuccess = function(aEvent) {
|
|
|
|
debug("Request successful. Id: " + aEvent.target.result);
|
2013-10-02 10:27:11 -07:00
|
|
|
self.addRevision(aRevisionStore, aEvent.target.result, REVISION_ADDED,
|
|
|
|
function() {
|
|
|
|
debug("AddInternal - revisionId increased");
|
|
|
|
// No wrap here because the result is always a int.
|
|
|
|
aResolve(aEvent.target.result);
|
|
|
|
}
|
|
|
|
);
|
2013-10-02 10:27:09 -07:00
|
|
|
};
|
|
|
|
},
|
|
|
|
|
2013-10-02 10:27:11 -07:00
|
|
|
removeInternal: function(aResolve, aStore, aRevisionStore, aId) {
|
2013-10-02 10:27:09 -07:00
|
|
|
debug("RemoveInternal");
|
|
|
|
|
2013-10-02 10:27:11 -07:00
|
|
|
let self = this;
|
|
|
|
let request = aStore.get(aId);
|
|
|
|
request.onsuccess = function(aEvent) {
|
|
|
|
debug("RemoveInternal success. Record: " + aEvent.target.result);
|
|
|
|
if (aEvent.target.result === undefined) {
|
|
|
|
aResolve(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let deleteRequest = aStore.delete(aId);
|
|
|
|
deleteRequest.onsuccess = function() {
|
|
|
|
debug("RemoveInternal success");
|
|
|
|
self.addRevision(aRevisionStore, aId, REVISION_REMOVED,
|
|
|
|
function() {
|
|
|
|
aResolve(true);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
};
|
2013-10-02 10:27:09 -07:00
|
|
|
};
|
|
|
|
},
|
|
|
|
|
2013-10-02 10:27:11 -07:00
|
|
|
clearInternal: function(aResolve, aStore, aRevisionStore) {
|
2013-10-02 10:27:09 -07:00
|
|
|
debug("ClearInternal");
|
|
|
|
|
2013-10-02 10:27:11 -07:00
|
|
|
let self = this;
|
2013-10-02 10:27:09 -07:00
|
|
|
let request = aStore.clear();
|
|
|
|
request.onsuccess = function() {
|
|
|
|
debug("ClearInternal success");
|
2013-10-02 10:27:11 -07:00
|
|
|
self.db.clearRevisions(aRevisionStore,
|
|
|
|
function() {
|
|
|
|
debug("Revisions cleared");
|
|
|
|
|
|
|
|
self.addRevision(aRevisionStore, 0, REVISION_VOID,
|
|
|
|
function() {
|
|
|
|
debug("ClearInternal - revisionId increased");
|
|
|
|
aResolve();
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
);
|
2013-10-02 10:27:09 -07:00
|
|
|
};
|
|
|
|
},
|
|
|
|
|
2013-10-02 10:27:11 -07:00
|
|
|
addRevision: function(aRevisionStore, aId, aType, aSuccessCb) {
|
|
|
|
let self = this;
|
|
|
|
this.db.addRevision(aRevisionStore, aId, aType,
|
|
|
|
function(aRevisionId) {
|
|
|
|
self.revisionId = aRevisionId;
|
2013-10-02 10:27:15 -07:00
|
|
|
self.sendNotification(aId, aType, aRevisionId);
|
2013-10-02 10:27:11 -07:00
|
|
|
aSuccessCb();
|
|
|
|
}
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
2013-10-02 10:27:15 -07:00
|
|
|
retrieveRevisionId: function(aSuccessCb, aForced) {
|
|
|
|
if (this.revisionId != null && !aForced) {
|
2013-10-02 10:27:11 -07:00
|
|
|
aSuccessCb();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let self = this;
|
|
|
|
this.db.revisionTxn(
|
|
|
|
'readwrite',
|
|
|
|
function(aTxn, aRevisionStore) {
|
|
|
|
debug("RetrieveRevisionId transaction success");
|
|
|
|
|
|
|
|
let request = aRevisionStore.openCursor(null, 'prev');
|
|
|
|
request.onsuccess = function(aEvent) {
|
|
|
|
let cursor = aEvent.target.result;
|
|
|
|
if (!cursor) {
|
|
|
|
// If the revision doesn't exist, let's create the first one.
|
2013-10-02 10:27:15 -07:00
|
|
|
self.addRevision(aRevisionStore, 0, REVISION_VOID,
|
|
|
|
function(aRevisionId) {
|
|
|
|
self.revisionId = aRevisionId;
|
|
|
|
aSuccessCb();
|
|
|
|
}
|
|
|
|
);
|
2013-10-02 10:27:11 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.revisionId = cursor.value.revisionId;
|
|
|
|
aSuccessCb();
|
|
|
|
};
|
|
|
|
}
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
2013-10-02 10:27:09 -07:00
|
|
|
throwInvalidArg: function(aWindow) {
|
|
|
|
return aWindow.Promise.reject(
|
|
|
|
new aWindow.DOMError("SyntaxError", "Non-numeric or invalid id"));
|
|
|
|
},
|
|
|
|
|
|
|
|
throwReadOnly: function(aWindow) {
|
|
|
|
return aWindow.Promise.reject(
|
|
|
|
new aWindow.DOMError("ReadOnlyError", "DataStore in readonly mode"));
|
|
|
|
},
|
|
|
|
|
2013-10-02 10:27:14 -07:00
|
|
|
exposeObject: function(aWindow, aReadOnly) {
|
2013-10-02 10:27:07 -07:00
|
|
|
let self = this;
|
2013-10-02 10:27:09 -07:00
|
|
|
let object = {
|
2013-10-02 10:27:15 -07:00
|
|
|
callbacks: [],
|
2013-10-02 10:27:09 -07:00
|
|
|
|
|
|
|
// Public interface :
|
|
|
|
|
2013-10-02 10:27:07 -07:00
|
|
|
get name() {
|
|
|
|
return self.name;
|
|
|
|
},
|
|
|
|
|
|
|
|
get owner() {
|
|
|
|
return self.owner;
|
|
|
|
},
|
|
|
|
|
|
|
|
get readOnly() {
|
2013-10-02 10:27:14 -07:00
|
|
|
return aReadOnly;
|
2013-10-02 10:27:07 -07:00
|
|
|
},
|
|
|
|
|
2013-10-02 10:27:09 -07:00
|
|
|
get: function DS_get(aId) {
|
|
|
|
aId = parseInt(aId);
|
|
|
|
if (isNaN(aId) || aId <= 0) {
|
|
|
|
return self.throwInvalidArg(aWindow);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Promise<Object>
|
|
|
|
return self.newDBPromise(aWindow, "readonly",
|
2013-10-02 10:27:11 -07:00
|
|
|
function(aResolve, aReject, aTxn, aStore, aRevisionStore) {
|
|
|
|
self.getInternal(aWindow, aResolve, aStore, aId);
|
2013-10-02 10:27:09 -07:00
|
|
|
}
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
update: function DS_update(aId, aObj) {
|
|
|
|
aId = parseInt(aId);
|
|
|
|
if (isNaN(aId) || aId <= 0) {
|
|
|
|
return self.throwInvalidArg(aWindow);
|
|
|
|
}
|
|
|
|
|
2013-10-02 10:27:14 -07:00
|
|
|
if (aReadOnly) {
|
2013-10-02 10:27:09 -07:00
|
|
|
return self.throwReadOnly(aWindow);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Promise<void>
|
|
|
|
return self.newDBPromise(aWindow, "readwrite",
|
2013-10-02 10:27:11 -07:00
|
|
|
function(aResolve, aReject, aTxn, aStore, aRevisionStore) {
|
|
|
|
self.updateInternal(aResolve, aStore, aRevisionStore, aId, aObj);
|
2013-10-02 10:27:09 -07:00
|
|
|
}
|
|
|
|
);
|
|
|
|
},
|
2013-10-02 10:27:07 -07:00
|
|
|
|
2013-10-02 10:27:09 -07:00
|
|
|
add: function DS_add(aObj) {
|
2013-10-02 10:27:14 -07:00
|
|
|
if (aReadOnly) {
|
2013-10-02 10:27:09 -07:00
|
|
|
return self.throwReadOnly(aWindow);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Promise<int>
|
|
|
|
return self.newDBPromise(aWindow, "readwrite",
|
2013-10-02 10:27:11 -07:00
|
|
|
function(aResolve, aReject, aTxn, aStore, aRevisionStore) {
|
|
|
|
self.addInternal(aResolve, aStore, aRevisionStore, aObj);
|
2013-10-02 10:27:09 -07:00
|
|
|
}
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
remove: function DS_remove(aId) {
|
|
|
|
aId = parseInt(aId);
|
|
|
|
if (isNaN(aId) || aId <= 0) {
|
|
|
|
return self.throwInvalidArg(aWindow);
|
|
|
|
}
|
|
|
|
|
2013-10-02 10:27:14 -07:00
|
|
|
if (aReadOnly) {
|
2013-10-02 10:27:09 -07:00
|
|
|
return self.throwReadOnly(aWindow);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Promise<void>
|
|
|
|
return self.newDBPromise(aWindow, "readwrite",
|
2013-10-02 10:27:11 -07:00
|
|
|
function(aResolve, aReject, aTxn, aStore, aRevisionStore) {
|
|
|
|
self.removeInternal(aResolve, aStore, aRevisionStore, aId);
|
2013-10-02 10:27:09 -07:00
|
|
|
}
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
clear: function DS_clear() {
|
2013-10-02 10:27:14 -07:00
|
|
|
if (aReadOnly) {
|
2013-10-02 10:27:09 -07:00
|
|
|
return self.throwReadOnly(aWindow);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Promise<void>
|
|
|
|
return self.newDBPromise(aWindow, "readwrite",
|
2013-10-02 10:27:11 -07:00
|
|
|
function(aResolve, aReject, aTxn, aStore, aRevisionStore) {
|
|
|
|
self.clearInternal(aResolve, aStore, aRevisionStore);
|
2013-10-02 10:27:09 -07:00
|
|
|
}
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
2013-10-02 10:27:11 -07:00
|
|
|
get revisionId() {
|
|
|
|
return self.revisionId;
|
|
|
|
},
|
|
|
|
|
|
|
|
getChanges: function(aRevisionId) {
|
|
|
|
debug("GetChanges: " + aRevisionId);
|
|
|
|
|
|
|
|
if (aRevisionId === null || aRevisionId === undefined) {
|
|
|
|
return aWindow.Promise.reject(
|
|
|
|
new aWindow.DOMError("SyntaxError", "Invalid revisionId"));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Promise<DataStoreChanges>
|
|
|
|
return new aWindow.Promise(function(aResolve, aReject) {
|
|
|
|
debug("GetChanges promise started");
|
|
|
|
self.db.revisionTxn(
|
|
|
|
'readonly',
|
|
|
|
function(aTxn, aStore) {
|
|
|
|
debug("GetChanges transaction success");
|
|
|
|
|
|
|
|
let request = self.db.getInternalRevisionId(
|
|
|
|
aRevisionId,
|
|
|
|
aStore,
|
|
|
|
function(aInternalRevisionId) {
|
|
|
|
if (aInternalRevisionId == undefined) {
|
|
|
|
aResolve(undefined);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This object is the return value of this promise.
|
|
|
|
// Initially we use maps, and then we convert them in array.
|
|
|
|
let changes = {
|
|
|
|
revisionId: '',
|
|
|
|
addedIds: {},
|
|
|
|
updatedIds: {},
|
|
|
|
removedIds: {}
|
|
|
|
};
|
|
|
|
|
|
|
|
let request = aStore.mozGetAll(aWindow.IDBKeyRange.lowerBound(aInternalRevisionId, true));
|
|
|
|
request.onsuccess = function(aEvent) {
|
|
|
|
for (let i = 0; i < aEvent.target.result.length; ++i) {
|
|
|
|
let data = aEvent.target.result[i];
|
|
|
|
|
|
|
|
switch (data.operation) {
|
|
|
|
case REVISION_ADDED:
|
|
|
|
changes.addedIds[data.objectId] = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case REVISION_UPDATED:
|
|
|
|
// We don't consider an update if this object has been added
|
|
|
|
// or if it has been already modified by a previous
|
|
|
|
// operation.
|
|
|
|
if (!(data.objectId in changes.addedIds) &&
|
|
|
|
!(data.objectId in changes.updatedIds)) {
|
|
|
|
changes.updatedIds[data.objectId] = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case REVISION_REMOVED:
|
|
|
|
let id = data.objectId;
|
|
|
|
|
|
|
|
// If the object has been added in this range of revisions
|
|
|
|
// we can ignore it and remove it from the list.
|
|
|
|
if (id in changes.addedIds) {
|
|
|
|
delete changes.addedIds[id];
|
|
|
|
} else {
|
|
|
|
changes.removedIds[id] = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (id in changes.updatedIds) {
|
|
|
|
delete changes.updatedIds[id];
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The last revisionId.
|
|
|
|
if (aEvent.target.result.length) {
|
|
|
|
changes.revisionId = aEvent.target.result[aEvent.target.result.length - 1].revisionId;
|
|
|
|
}
|
|
|
|
|
|
|
|
// From maps to arrays.
|
|
|
|
changes.addedIds = Object.keys(changes.addedIds).map(function(aKey) { return parseInt(aKey, 10); });
|
|
|
|
changes.updatedIds = Object.keys(changes.updatedIds).map(function(aKey) { return parseInt(aKey, 10); });
|
|
|
|
changes.removedIds = Object.keys(changes.removedIds).map(function(aKey) { return parseInt(aKey, 10); });
|
|
|
|
|
|
|
|
let wrappedObject = ObjectWrapper.wrap(changes, aWindow);
|
|
|
|
aResolve(wrappedObject);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
);
|
|
|
|
},
|
|
|
|
function(aEvent) {
|
|
|
|
debug("GetChanges transaction failed");
|
|
|
|
aReject(createDOMError(aWindow, aEvent));
|
|
|
|
}
|
|
|
|
);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2013-10-02 10:27:15 -07:00
|
|
|
set onchange(aCallback) {
|
|
|
|
debug("Set OnChange");
|
|
|
|
this.onchangeCb = aCallback;
|
|
|
|
},
|
|
|
|
|
|
|
|
get onchange() {
|
|
|
|
debug("Get OnChange");
|
|
|
|
return this.onchangeCb;
|
|
|
|
},
|
|
|
|
|
|
|
|
addEventListener: function(aName, aCallback) {
|
|
|
|
debug("addEventListener:" + aName);
|
|
|
|
if (aName != 'change') {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.callbacks.push(aCallback);
|
|
|
|
},
|
|
|
|
|
|
|
|
removeEventListener: function(aName, aCallback) {
|
|
|
|
debug('removeEventListener');
|
|
|
|
let pos = this.callbacks.indexOf(aCallback);
|
|
|
|
if (pos != -1) {
|
|
|
|
this.callbacks.splice(pos, 1);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2013-10-02 10:27:09 -07:00
|
|
|
/* TODO:
|
2013-10-02 10:27:07 -07:00
|
|
|
getAll(), getLength()
|
|
|
|
*/
|
|
|
|
|
|
|
|
__exposedProps__: {
|
|
|
|
name: 'r',
|
|
|
|
owner: 'r',
|
2013-10-02 10:27:09 -07:00
|
|
|
readOnly: 'r',
|
|
|
|
get: 'r',
|
|
|
|
update: 'r',
|
|
|
|
add: 'r',
|
|
|
|
remove: 'r',
|
2013-10-02 10:27:11 -07:00
|
|
|
clear: 'r',
|
|
|
|
revisionId: 'r',
|
2013-10-02 10:27:15 -07:00
|
|
|
getChanges: 'r',
|
|
|
|
onchange: 'rw',
|
|
|
|
addEventListener: 'r',
|
|
|
|
removeEventListener: 'r'
|
|
|
|
},
|
|
|
|
|
|
|
|
receiveMessage: function(aMessage) {
|
|
|
|
debug("receiveMessage");
|
|
|
|
|
|
|
|
if (aMessage.name != "DataStore:Changed:Return:OK") {
|
|
|
|
debug("Wrong message: " + aMessage.name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.retrieveRevisionId(
|
|
|
|
function() {
|
|
|
|
if (object.onchangeCb || object.callbacks.length) {
|
|
|
|
let wrappedData = ObjectWrapper.wrap(aMessage.data, aWindow);
|
|
|
|
|
|
|
|
// This array is used to avoid that a callback adds/removes
|
|
|
|
// another eventListener.
|
|
|
|
var cbs = [];
|
|
|
|
if (object.onchangeCb) {
|
|
|
|
cbs.push(object.onchangeCb);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let i = 0; i < object.callbacks.length; ++i) {
|
|
|
|
cbs.push(object.callbacks[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let i = 0; i < cbs.length; ++i) {
|
|
|
|
try {
|
|
|
|
cbs[i](wrappedData);
|
|
|
|
} catch(e) {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// Forcing the reading of the revisionId
|
|
|
|
true
|
|
|
|
);
|
2013-10-02 10:27:07 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-10-02 10:27:15 -07:00
|
|
|
Services.obs.addObserver(function(aSubject, aTopic, aData) {
|
|
|
|
let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
|
|
|
|
if (wId == object.innerWindowID) {
|
|
|
|
cpmm.removeMessageListener("DataStore:Changed:Return:OK", object);
|
|
|
|
}
|
|
|
|
}, "inner-window-destroyed", false);
|
|
|
|
|
|
|
|
let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
|
|
.getInterface(Ci.nsIDOMWindowUtils);
|
|
|
|
object.innerWindowID = util.currentInnerWindowID;
|
|
|
|
|
|
|
|
cpmm.addMessageListener("DataStore:Changed:Return:OK", object);
|
|
|
|
cpmm.sendAsyncMessage("DataStore:RegisterForMessages",
|
|
|
|
{ store: this.name, owner: this.owner });
|
|
|
|
|
2013-10-02 10:27:09 -07:00
|
|
|
return object;
|
|
|
|
},
|
|
|
|
|
|
|
|
delete: function() {
|
|
|
|
this.db.delete();
|
2013-10-02 10:27:15 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
sendNotification: function(aId, aOperation, aRevisionId) {
|
|
|
|
debug("SendNotification");
|
|
|
|
if (aOperation != REVISION_VOID) {
|
|
|
|
cpmm.sendAsyncMessage("DataStore:Changed",
|
|
|
|
{ store: this.name, owner: this.owner,
|
|
|
|
message: { revisionId: aRevisionId, id: aId,
|
|
|
|
operation: aOperation } } );
|
|
|
|
}
|
2013-10-02 10:27:07 -07:00
|
|
|
}
|
|
|
|
};
|
2013-10-02 10:27:14 -07:00
|
|
|
|
|
|
|
/* DataStoreAccess */
|
|
|
|
|
|
|
|
function DataStoreAccess(aAppId, aName, aOrigin, aReadOnly) {
|
|
|
|
this.appId = aAppId;
|
|
|
|
this.name = aName;
|
|
|
|
this.origin = aOrigin;
|
|
|
|
this.readOnly = aReadOnly;
|
|
|
|
}
|
|
|
|
|
|
|
|
DataStoreAccess.prototype = {};
|
|
|
|
|