From 263e781a7375d7003637367e14fc19bed5d695a4 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Wed, 2 Oct 2013 13:27:15 -0400 Subject: [PATCH] Bug 871445 - patch 9 - DataStore: child<->parent communication, r=ehsan --HG-- rename : dom/datastore/tests/test_basic.html => dom/datastore/tests/test_oop.html --- dom/datastore/DataStore.jsm | 617 ++++++++++------------ dom/datastore/DataStoreChangeNotifier.jsm | 2 - dom/datastore/DataStoreService.js | 180 ++++--- dom/datastore/tests/Makefile.in | 1 + dom/datastore/tests/test_oop.html | 129 +++++ 5 files changed, 537 insertions(+), 392 deletions(-) create mode 100644 dom/datastore/tests/test_oop.html diff --git a/dom/datastore/DataStore.jsm b/dom/datastore/DataStore.jsm index 125d85edee8..7277ce57002 100644 --- a/dom/datastore/DataStore.jsm +++ b/dom/datastore/DataStore.jsm @@ -6,7 +6,7 @@ 'use strict' -this.EXPORTED_SYMBOLS = ["DataStore", "DataStoreAccess"]; +this.EXPORTED_SYMBOLS = ["DataStore"]; function debug(s) { // dump('DEBUG DataStore: ' + s + '\n'); @@ -67,13 +67,13 @@ function parseIds(aId) { return aId; } -/* Exposed DataStore object */ -function ExposedDataStore(aWindow, aDataStore, aReadOnly) { - debug("ExposedDataStore created"); - this.init(aWindow, aDataStore, aReadOnly); +/* DataStore object */ +this.DataStore = function(aWindow, aName, aOwner, aReadOnly) { + debug("DataStore created"); + this.init(aWindow, aName, aOwner, aReadOnly); } -ExposedDataStore.prototype = { +this.DataStore.prototype = { classDescription: "DataStore XPCOM Component", classID: Components.ID("{db5c9602-030f-4bff-a3de-881a8de370f2}"), contractID: "@mozilla.org/dom/datastore;1", @@ -81,286 +81,46 @@ ExposedDataStore.prototype = { callbacks: [], - init: function(aWindow, aDataStore, aReadOnly) { - debug("ExposedDataStore init"); + _window: null, + _name: null, + _owner: null, + _readOnly: null, + _revisionId: null, - this.window = aWindow; - this.dataStore = aDataStore; - this.isReadOnly = aReadOnly; - }, + init: function(aWindow, aName, aOwner, aReadOnly) { + debug("DataStore init"); - receiveMessage: function(aMessage) { - debug("receiveMessage"); + this._window = aWindow; + this._name = aName; + this._owner = aOwner; + this._readOnly = aReadOnly; - if (aMessage.name != "DataStore:Changed:Return:OK") { - debug("Wrong message: " + aMessage.name); - return; - } + this._db = new DataStoreDB(); + this._db.init(aOwner, aName); let self = this; - - this.dataStore.retrieveRevisionId( - function() { - let event = new self.window.DataStoreChangeEvent('change', aMessage.data); - self.__DOM_IMPL__.dispatchEvent(event); - }, - // Forcing the reading of the revisionId - true - ); - }, - - // Public interface : - - get name() { - return this.dataStore.name; - }, - - get owner() { - return this.dataStore.owner; - }, - - get readOnly() { - return this.isReadOnly; - }, - - get: function(aId) { - aId = parseIds(aId); - if (aId === null) { - return throwInvalidArg(this.window); - } - - let self = this; - - // Promise - return this.dataStore.newDBPromise(this.window, "readonly", - function(aResolve, aReject, aTxn, aStore, aRevisionStore) { - self.dataStore.getInternal(aStore, - Array.isArray(aId) ? aId : [ aId ], - function(aResults) { - aResolve(Array.isArray(aId) ? aResults : aResults[0]); - }); + Services.obs.addObserver(function(aSubject, aTopic, aData) { + let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data; + if (wId == self._innerWindowID) { + cpmm.removeMessageListener("DataStore:Changed:Return:OK", self); + self._db.delete(); } - ); + }, "inner-window-destroyed", false); + + let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + this._innerWindowID = util.currentInnerWindowID; + + cpmm.addMessageListener("DataStore:Changed:Return:OK", this); + cpmm.sendAsyncMessage("DataStore:RegisterForMessages", + { store: this._name, owner: this._owner }); }, - update: function(aId, aObj) { - aId = parseInt(aId); - if (isNaN(aId) || aId <= 0) { - return throwInvalidArg(this.window); - } - - if (this.isReadOnly) { - return throwReadOnly(this.window); - } - + newDBPromise: function(aTxnType, aFunction) { let self = this; - - // Promise - return this.dataStore.newDBPromise(this.window, "readwrite", - function(aResolve, aReject, aTxn, aStore, aRevisionStore) { - self.dataStore.updateInternal(aResolve, aStore, aRevisionStore, aId, aObj); - } - ); - }, - - add: function(aObj) { - if (this.isReadOnly) { - return throwReadOnly(this.window); - } - - let self = this; - - // Promise - return this.dataStore.newDBPromise(this.window, "readwrite", - function(aResolve, aReject, aTxn, aStore, aRevisionStore) { - self.dataStore.addInternal(aResolve, aStore, aRevisionStore, aObj); - } - ); - }, - - remove: function(aId) { - aId = parseInt(aId); - if (isNaN(aId) || aId <= 0) { - return throwInvalidArg(this.window); - } - - if (this.isReadOnly) { - return throwReadOnly(this.window); - } - - let self = this; - - // Promise - return this.dataStore.newDBPromise(this.window, "readwrite", - function(aResolve, aReject, aTxn, aStore, aRevisionStore) { - self.dataStore.removeInternal(aResolve, aStore, aRevisionStore, aId); - } - ); - }, - - clear: function() { - if (this.isReadOnly) { - return throwReadOnly(this.window); - } - - let self = this; - - // Promise - return this.dataStore.newDBPromise(this.window, "readwrite", - function(aResolve, aReject, aTxn, aStore, aRevisionStore) { - self.dataStore.clearInternal(aResolve, aStore, aRevisionStore); - } - ); - }, - - get revisionId() { - return this.dataStore.revisionId; - }, - - getChanges: function(aRevisionId) { - debug("GetChanges: " + aRevisionId); - - if (aRevisionId === null || aRevisionId === undefined) { - return this.window.Promise.reject( - new this.window.DOMError("SyntaxError", "Invalid revisionId")); - } - - let self = this; - - // Promise - return new this.window.Promise(function(aResolve, aReject) { - debug("GetChanges promise started"); - self.dataStore.db.revisionTxn( - 'readonly', - function(aTxn, aStore) { - debug("GetChanges transaction success"); - - let request = self.dataStore.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(self.window.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, self.window); - aResolve(wrappedObject); - }; - } - ); - }, - function(aEvent) { - debug("GetChanges transaction failed"); - aReject(createDOMError(self.window, aEvent)); - } - ); - }); - }, - - getLength: function() { - let self = this; - - // Promise - return this.dataStore.newDBPromise(this.window, "readonly", - function(aResolve, aReject, aTxn, aStore, aRevisionStore) { - self.dataStore.getLengthInternal(aResolve, aStore); - } - ); - }, - - set onchange(aCallback) { - debug("Set OnChange"); - this.__DOM_IMPL__.setEventHandler("onchange", aCallback); - }, - - get onchange() { - debug("Get OnChange"); - return this.__DOM_IMPL__.getEventHandler("onchange"); - } -}; - -/* DataStore object */ - -function DataStore(aAppId, aName, aOwner, aReadOnly, aGlobalScope) { - this.appId = aAppId; - this.name = aName; - this.owner = aOwner; - this.readOnly = aReadOnly; - - this.db = new DataStoreDB(); - this.db.init(aOwner, aName, aGlobalScope); -} - -DataStore.prototype = { - appId: null, - name: null, - owner: null, - readOnly: null, - revisionId: null, - - newDBPromise: function(aWindow, aTxnType, aFunction) { - let db = this.db; - return new aWindow.Promise(function(aResolve, aReject) { + return new this._window.Promise(function(aResolve, aReject) { debug("DBPromise started"); - db.txn( + self._db.txn( aTxnType, function(aTxn, aStore, aRevisionStore) { debug("DBPromise success"); @@ -368,7 +128,7 @@ DataStore.prototype = { }, function(aEvent) { debug("DBPromise error"); - aReject(createDOMError(aWindow, aEvent)); + aReject(createDOMError(self._window, aEvent)); } ); }); @@ -478,7 +238,7 @@ DataStore.prototype = { let request = aStore.clear(); request.onsuccess = function() { debug("ClearInternal success"); - self.db.clearRevisions(aRevisionStore, + self._db.clearRevisions(aRevisionStore, function() { debug("Revisions cleared"); @@ -506,23 +266,18 @@ DataStore.prototype = { addRevision: function(aRevisionStore, aId, aType, aSuccessCb) { let self = this; - this.db.addRevision(aRevisionStore, aId, aType, + this._db.addRevision(aRevisionStore, aId, aType, function(aRevisionId) { - self.revisionId = aRevisionId; + self._revisionId = aRevisionId; self.sendNotification(aId, aType, aRevisionId); aSuccessCb(); } ); }, - retrieveRevisionId: function(aSuccessCb, aForced) { - if (this.revisionId != null && !aForced) { - aSuccessCb(); - return; - } - + retrieveRevisionId: function(aSuccessCb) { let self = this; - this.db.revisionTxn( + this._db.revisionTxn( 'readwrite', function(aTxn, aRevisionStore) { debug("RetrieveRevisionId transaction success"); @@ -534,48 +289,20 @@ DataStore.prototype = { // If the revision doesn't exist, let's create the first one. self.addRevision(aRevisionStore, 0, REVISION_VOID, function(aRevisionId) { - self.revisionId = aRevisionId; + self._revisionId = aRevisionId; aSuccessCb(); } ); return; } - self.revisionId = cursor.value.revisionId; + self._revisionId = cursor.value.revisionId; aSuccessCb(); }; } ); }, - exposeObject: function(aWindow, aReadOnly) { - debug("Exposing Object"); - - let exposedObject = new ExposedDataStore(aWindow, this, aReadOnly); - let object = aWindow.DataStore._create(aWindow, exposedObject); - - Services.obs.addObserver(function(aSubject, aTopic, aData) { - let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data; - if (wId == exposedObject.innerWindowID) { - cpmm.removeMessageListener("DataStore:Changed:Return:OK", exposedObject); - } - }, "inner-window-destroyed", false); - - let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - exposedObject.innerWindowID = util.currentInnerWindowID; - - cpmm.addMessageListener("DataStore:Changed:Return:OK", exposedObject); - cpmm.sendAsyncMessage("DataStore:RegisterForMessages", - { store: this.name, owner: this.owner }); - - return object; - }, - - delete: function() { - this.db.delete(); - }, - sendNotification: function(aId, aOperation, aRevisionId) { debug("SendNotification"); if (aOperation != REVISION_VOID) { @@ -584,16 +311,250 @@ DataStore.prototype = { message: { revisionId: aRevisionId, id: aId, operation: aOperation } } ); } + }, + + receiveMessage: function(aMessage) { + debug("receiveMessage"); + + if (aMessage.name != "DataStore:Changed:Return:OK") { + debug("Wrong message: " + aMessage.name); + return; + } + + let self = this; + + this.retrieveRevisionId( + function() { + let event = new self._window.DataStoreChangeEvent('change', aMessage.data); + self.__DOM_IMPL__.dispatchEvent(event); + } + ); + }, + + // Public interface : + + get name() { + return this._name; + }, + + get owner() { + return this._owner; + }, + + get readOnly() { + return this._readOnly; + }, + + get: function(aId) { + aId = parseIds(aId); + if (aId === null) { + return throwInvalidArg(this._window); + } + + let self = this; + + // Promise + return this.newDBPromise("readonly", + function(aResolve, aReject, aTxn, aStore, aRevisionStore) { + self.getInternal(aStore, + Array.isArray(aId) ? aId : [ aId ], + function(aResults) { + aResolve(Array.isArray(aId) ? aResults : aResults[0]); + }); + } + ); + }, + + update: function(aId, aObj) { + aId = parseInt(aId); + if (isNaN(aId) || aId <= 0) { + return throwInvalidArg(this._window); + } + + if (this._readOnly) { + return throwReadOnly(this._window); + } + + let self = this; + + // Promise + return this.newDBPromise("readwrite", + function(aResolve, aReject, aTxn, aStore, aRevisionStore) { + self.updateInternal(aResolve, aStore, aRevisionStore, aId, aObj); + } + ); + }, + + add: function(aObj) { + if (this._readOnly) { + return throwReadOnly(this._window); + } + + let self = this; + + // Promise + return this.newDBPromise("readwrite", + function(aResolve, aReject, aTxn, aStore, aRevisionStore) { + self.addInternal(aResolve, aStore, aRevisionStore, aObj); + } + ); + }, + + remove: function(aId) { + aId = parseInt(aId); + if (isNaN(aId) || aId <= 0) { + return throwInvalidArg(this._window); + } + + if (this._readOnly) { + return throwReadOnly(this._window); + } + + let self = this; + + // Promise + return this.newDBPromise("readwrite", + function(aResolve, aReject, aTxn, aStore, aRevisionStore) { + self.removeInternal(aResolve, aStore, aRevisionStore, aId); + } + ); + }, + + clear: function() { + if (this._readOnly) { + return throwReadOnly(this._window); + } + + let self = this; + + // Promise + return this.newDBPromise("readwrite", + function(aResolve, aReject, aTxn, aStore, aRevisionStore) { + self.clearInternal(aResolve, aStore, aRevisionStore); + } + ); + }, + + get revisionId() { + return this._revisionId; + }, + + getChanges: function(aRevisionId) { + debug("GetChanges: " + aRevisionId); + + if (aRevisionId === null || aRevisionId === undefined) { + return this._window.Promise.reject( + new this._window.DOMError("SyntaxError", "Invalid revisionId")); + } + + let self = this; + + // Promise + return new this._window.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(self._window.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, self._window); + aResolve(wrappedObject); + }; + } + ); + }, + function(aEvent) { + debug("GetChanges transaction failed"); + aReject(createDOMError(self._window, aEvent)); + } + ); + }); + }, + + getLength: function() { + let self = this; + + // Promise + return this.newDBPromise("readonly", + function(aResolve, aReject, aTxn, aStore, aRevisionStore) { + self.getLengthInternal(aResolve, aStore); + } + ); + }, + + set onchange(aCallback) { + debug("Set OnChange"); + this.__DOM_IMPL__.setEventHandler("onchange", aCallback); + }, + + get onchange() { + debug("Get OnChange"); + return this.__DOM_IMPL__.getEventHandler("onchange"); } }; - -/* DataStoreAccess */ - -function DataStoreAccess(aAppId, aName, aOrigin, aReadOnly) { - this.appId = aAppId; - this.name = aName; - this.origin = aOrigin; - this.readOnly = aReadOnly; -} - -DataStoreAccess.prototype = {}; diff --git a/dom/datastore/DataStoreChangeNotifier.jsm b/dom/datastore/DataStoreChangeNotifier.jsm index a20de55cf0d..53ca45e4654 100644 --- a/dom/datastore/DataStoreChangeNotifier.jsm +++ b/dom/datastore/DataStoreChangeNotifier.jsm @@ -15,8 +15,6 @@ function debug(s) { Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); -const kFromDataStoreChangeNotifier = "fromDataStoreChangeNotifier"; - XPCOMUtils.defineLazyServiceGetter(this, "ppmm", "@mozilla.org/parentprocessmessagemanager;1", "nsIMessageBroadcaster"); diff --git a/dom/datastore/DataStoreService.js b/dom/datastore/DataStoreService.js index d2b9a82b873..c75766f7edd 100644 --- a/dom/datastore/DataStoreService.js +++ b/dom/datastore/DataStoreService.js @@ -17,6 +17,15 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; Cu.import('resource://gre/modules/XPCOMUtils.jsm'); Cu.import('resource://gre/modules/Services.jsm'); Cu.import('resource://gre/modules/DataStore.jsm'); +Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); + +XPCOMUtils.defineLazyServiceGetter(this, "cpmm", + "@mozilla.org/childprocessmessagemanager;1", + "nsIMessageSender"); + +XPCOMUtils.defineLazyServiceGetter(this, "ppmm", + "@mozilla.org/parentprocessmessagemanager;1", + "nsIMessageBroadcaster"); /* DataStoreService */ @@ -32,6 +41,12 @@ function DataStoreService() { } obs.addObserver(this, 'webapps-clear-data', false); + + let inParent = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime) + .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT; + if (inParent) { + ppmm.addMessageListener("DataStore:Get", this); + } } DataStoreService.prototype = { @@ -49,13 +64,11 @@ DataStoreService.prototype = { return; } - let store = new DataStore(aAppId, aName, aOwner, aReadOnly); - if (!(aName in this.stores)) { this.stores[aName] = {}; } - this.stores[aName][aAppId] = store; + this.stores[aName][aAppId] = { owner: aOwner, readOnly: aReadOnly }; }, installAccessDataStore: function(aAppId, aName, aOwner, aReadOnly) { @@ -68,80 +81,69 @@ DataStoreService.prototype = { return; } - let accessStore = new DataStoreAccess(aAppId, aName, aOwner, aReadOnly); - if (!(aName in this.accessStores)) { this.accessStores[aName] = {}; } - this.accessStores[aName][aAppId] = accessStore; + this.accessStores[aName][aAppId] = { owner: aOwner, readOnly: aReadOnly }; }, getDataStores: function(aWindow, aName) { debug('getDataStores - aName: ' + aName); - let appId = aWindow.document.nodePrincipal.appId; - let self = this; + // This method can be called in the child so we need to send a request to + // the parent and create DataStore object here. + return new aWindow.Promise(function(resolve, reject) { - let matchingStores = []; - - if (aName in self.stores) { - if (appId in self.stores[aName]) { - matchingStores.push({ store: self.stores[aName][appId], - readonly: false }); - } - - for (var i in self.stores[aName]) { - if (i == appId) { - continue; - } - - let access = self.getDataStoreAccess(self.stores[aName][i], appId); - if (!access) { - continue; - } - - let readOnly = self.stores[aName][i].readOnly || access.readOnly; - matchingStores.push({ store: self.stores[aName][i], - readonly: readOnly }); - } - } - - let callbackPending = matchingStores.length; - let results = []; - - if (!callbackPending) { - resolve(results); - return; - } - - for (let i = 0; i < matchingStores.length; ++i) { - let obj = matchingStores[i].store.exposeObject(aWindow, - matchingStores[i].readonly); - results.push(obj); - - matchingStores[i].store.retrieveRevisionId( - function() { - --callbackPending; - if (!callbackPending) { - resolve(results); - } - }, - // if the revision is already known, we don't need to retrieve it - // again. - false - ); - } + new DataStoreServiceChild(aWindow, aName, resolve); }); }, - getDataStoreAccess: function(aStore, aAppId) { - if (!(aStore.name in this.accessStores) || - !(aAppId in this.accessStores[aStore.name])) { + receiveMessage: function(aMessage) { + if (aMessage.name != 'DataStore:Get') { + return; + } + + let msg = aMessage.data; + + // This is a security issue and it will be fixed by Bug 916091 + let appId = msg.appId; + + let results = []; + + if (msg.name in this.stores) { + if (appId in this.stores[msg.name]) { + results.push({ store: this.stores[msg.name][appId], + readOnly: false }); + } + + for (var i in this.stores[msg.name]) { + if (i == appId) { + continue; + } + + let access = this.getDataStoreAccess(msg.name, appId); + if (!access) { + continue; + } + + let readOnly = this.stores[msg.name][i].readOnly || access.readOnly; + results.push({ store: this.stores[msg.name][i], + readOnly: readOnly }); + } + } + + msg.stores = results; + aMessage.target.sendAsyncMessage("DataStore:Get:Return", msg); + }, + + getDataStoreAccess: function(aName, aAppId) { + if (!(aName in this.accessStores) || + !(aAppId in this.accessStores[aName])) { return null; } - return this.accessStores[aStore.name][aAppId]; + return this.accessStores[aName][aAppId]; }, observe: function observe(aSubject, aTopic, aData) { @@ -160,7 +162,6 @@ DataStoreService.prototype = { for (let key in this.stores) { if (params.appId in this.stores[key]) { - this.stores[key][params.appId].delete(); delete this.stores[key][params.appId]; } @@ -181,4 +182,59 @@ DataStoreService.prototype = { }) }; +/* DataStoreServiceChild */ + +function DataStoreServiceChild(aWindow, aName, aResolve) { + debug("DataStoreServiceChild created"); + this.init(aWindow, aName, aResolve); +} + +DataStoreServiceChild.prototype = { + __proto__: DOMRequestIpcHelper.prototype, + + init: function(aWindow, aName, aResolve) { + this._window = aWindow; + this._name = aName; + this._resolve = aResolve; + + this.initDOMRequestHelper(aWindow, [ "DataStore:Get:Return" ]); + + // This is a security issue and it will be fixed by Bug 916091 + cpmm.sendAsyncMessage("DataStore:Get", + { name: aName, appId: aWindow.document.nodePrincipal.appId }); + }, + + receiveMessage: function(aMessage) { + if (aMessage.name != 'DataStore:Get:Return') { + return; + } + let msg = aMessage.data; + let self = this; + + let callbackPending = msg.stores.length; + let results = []; + + if (!callbackPending) { + this._resolve(results); + return; + } + + for (let i = 0; i < msg.stores.length; ++i) { + let obj = new DataStore(this._window, this._name, + msg.stores[i].owner, msg.stores[i].readOnly); + let exposedObj = this._window.DataStore._create(this._window, obj); + results.push(exposedObj); + + obj.retrieveRevisionId( + function() { + --callbackPending; + if (!callbackPending) { + self._resolve(results); + } + } + ); + } + } +} + this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DataStoreService]); diff --git a/dom/datastore/tests/Makefile.in b/dom/datastore/tests/Makefile.in index e77bd21fc3f..6d82c99d893 100644 --- a/dom/datastore/tests/Makefile.in +++ b/dom/datastore/tests/Makefile.in @@ -28,6 +28,7 @@ MOCHITEST_FILES = \ file_app2.template.webapp \ test_arrays.html \ file_arrays.html \ + test_oop.html \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/dom/datastore/tests/test_oop.html b/dom/datastore/tests/test_oop.html new file mode 100644 index 00000000000..a8fd07cdbef --- /dev/null +++ b/dom/datastore/tests/test_oop.html @@ -0,0 +1,129 @@ + + + + + Test for DataStore - basic operation on a readonly db + + + + +
+ + +