From 7faee0e1e4ab9e56c769d6a11e064a7c28cc7292 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Wed, 2 Oct 2013 11:30:34 -0400 Subject: [PATCH] Bug 871445 - patch 10 - DataStore: indexedDB permissions, r=ehsan --- dom/apps/src/Webapps.jsm | 32 +- dom/datastore/DataStore.jsm | 20 +- dom/datastore/DataStoreChangeNotifier.jsm | 3 + dom/datastore/DataStoreDB.jsm | 4 +- dom/datastore/DataStoreService.js | 367 ++++++++++++++++----- dom/datastore/DataStoreServiceInternal.jsm | 51 +++ dom/datastore/moz.build | 1 + dom/datastore/nsIDataStoreService.idl | 12 +- dom/datastore/tests/test_app_install.html | 8 +- dom/datastore/tests/test_arrays.html | 6 +- dom/datastore/tests/test_basic.html | 6 +- dom/datastore/tests/test_changes.html | 6 +- dom/datastore/tests/test_oop.html | 6 +- dom/datastore/tests/test_readonly.html | 11 +- dom/datastore/tests/test_revision.html | 6 +- testing/mochitest/manifest.webapp | 4 + 16 files changed, 427 insertions(+), 116 deletions(-) create mode 100644 dom/datastore/DataStoreServiceInternal.jsm diff --git a/dom/apps/src/Webapps.jsm b/dom/apps/src/Webapps.jsm index af4c5ec6921..4986c1c186e 100644 --- a/dom/apps/src/Webapps.jsm +++ b/dom/apps/src/Webapps.jsm @@ -284,6 +284,7 @@ this.DOMApplicationRegistry = { // Create or Update the DataStore for this app this._readManifests([{ id: aId }], (function(aResult) { this.updateDataStore(this.webapps[aId].localId, + this.webapps[aId].origin, this.webapps[aId].manifestURL, aResult[0].manifest); }).bind(this)); @@ -530,12 +531,17 @@ this.DOMApplicationRegistry = { } this.updateOfflineCacheForApp(id); this.updatePermissionsForApp(id); - this.updateDataStoreForApp(id); } // Need to update the persisted list of apps since // installPreinstalledApp() removes the ones failing to install. this._saveApps(); } + + // DataStores must be initialized at startup. + for (let id in this.webapps) { + this.updateDataStoreForApp(id); + } + this.registerAppsHandlers(runUpdate); }).bind(this); @@ -552,14 +558,15 @@ this.DOMApplicationRegistry = { }).bind(this)); }, - updateDataStore: function(aId, aManifestURL, aManifest) { + updateDataStore: function(aId, aOrigin, aManifestURL, aManifest) { if ('datastores-owned' in aManifest) { for (let name in aManifest['datastores-owned']) { let readonly = "access" in aManifest['datastores-owned'][name] ? aManifest['datastores-owned'][name].access == 'readonly' : false; - dataStoreService.installDataStore(aId, name, aManifestURL, readonly); + dataStoreService.installDataStore(aId, name, aOrigin, aManifestURL, + readonly); } } @@ -569,9 +576,8 @@ this.DOMApplicationRegistry = { !aManifest['datastores-access'][name].readonly ? false : true; - // The first release is always in readonly mode. - dataStoreService.installAccessDataStore(aId, name, aManifestURL, - /* readonly */ true); + dataStoreService.installAccessDataStore(aId, name, aOrigin, + aManifestURL, readonly); } } }, @@ -1434,7 +1440,8 @@ this.DOMApplicationRegistry = { manifestURL: app.manifestURL }, true); } - this.updateDataStore(this.webapps[id].localId, app.manifestURL, aData); + this.updateDataStore(this.webapps[id].localId, app.origin, + app.manifestURL, aData); this.broadcastMessage("Webapps:PackageEvent", { type: "applied", manifestURL: app.manifestURL, @@ -1640,7 +1647,8 @@ this.DOMApplicationRegistry = { }, true); } - this.updateDataStore(this.webapps[id].localId, app.manifestURL, app.manifest); + this.updateDataStore(this.webapps[id].localId, app.origin, + app.manifestURL, app.manifest); app.name = manifest.name; app.csp = manifest.csp || ""; @@ -2142,8 +2150,8 @@ this.DOMApplicationRegistry = { true); } - this.updateDataStore(this.webapps[aId].localId, appObject.manifestURL, - aManifest); + this.updateDataStore(this.webapps[aId].localId, appObject.origin, + appObject.manifestURL, aManifest); debug("About to fire Webapps:PackageEvent 'installed'"); this.broadcastMessage("Webapps:PackageEvent", @@ -2248,8 +2256,8 @@ this.DOMApplicationRegistry = { }).bind(this)); } - this.updateDataStore(this.webapps[id].localId, this.webapps[id].manifestURL, - jsonManifest); + this.updateDataStore(this.webapps[id].localId, this.webapps[id].origin, + this.webapps[id].manifestURL, jsonManifest); } ["installState", "downloadAvailable", diff --git a/dom/datastore/DataStore.jsm b/dom/datastore/DataStore.jsm index 7277ce57002..e017a4e92b4 100644 --- a/dom/datastore/DataStore.jsm +++ b/dom/datastore/DataStore.jsm @@ -103,7 +103,7 @@ this.DataStore.prototype = { let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data; if (wId == self._innerWindowID) { cpmm.removeMessageListener("DataStore:Changed:Return:OK", self); - self._db.delete(); + self._db.close(); } }, "inner-window-destroyed", false); @@ -278,26 +278,18 @@ this.DataStore.prototype = { retrieveRevisionId: function(aSuccessCb) { let self = this; this._db.revisionTxn( - 'readwrite', + 'readonly', 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. - self.addRevision(aRevisionStore, 0, REVISION_VOID, - function(aRevisionId) { - self._revisionId = aRevisionId; - aSuccessCb(); - } - ); - return; + if (cursor) { + self._revisionId = cursor.value.revisionId; } - self._revisionId = cursor.value.revisionId; - aSuccessCb(); + aSuccessCb(self._revisionId); }; } ); @@ -475,7 +467,7 @@ this.DataStore.prototype = { removedIds: {} }; - let request = aStore.mozGetAll(self._window.IDBKeyRange.lowerBound(aInternalRevisionId, true)); + let request = aStore.mozGetAll(IDBKeyRange.lowerBound(aInternalRevisionId, true)); request.onsuccess = function(aEvent) { for (let i = 0; i < aEvent.target.result.length; ++i) { let data = aEvent.target.result[i]; diff --git a/dom/datastore/DataStoreChangeNotifier.jsm b/dom/datastore/DataStoreChangeNotifier.jsm index 53ca45e4654..f17f14dda0d 100644 --- a/dom/datastore/DataStoreChangeNotifier.jsm +++ b/dom/datastore/DataStoreChangeNotifier.jsm @@ -12,6 +12,9 @@ function debug(s) { //dump('DEBUG DataStoreChangeNotifier: ' + s + '\n'); } +// DataStoreServiceInternal should not be converted into a lazy getter as it +// runs code during initialization. +Cu.import('resource://gre/modules/DataStoreServiceInternal.jsm'); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); diff --git a/dom/datastore/DataStoreDB.jsm b/dom/datastore/DataStoreDB.jsm index 0a40d0efc0e..b1c81a843c0 100644 --- a/dom/datastore/DataStoreDB.jsm +++ b/dom/datastore/DataStoreDB.jsm @@ -39,8 +39,8 @@ DataStoreDB.prototype = { store.createIndex(DATASTOREDB_REVISION_INDEX, 'revisionId', { unique: true }); }, - init: function(aOrigin, aName) { - let dbName = aOrigin + '_' + aName; + init: function(aOwner, aName) { + let dbName = aName + '|' + aOwner; this.initDBHelper(dbName, DATASTOREDB_VERSION, [DATASTOREDB_OBJECTSTORE_NAME, DATASTOREDB_REVISION]); }, diff --git a/dom/datastore/DataStoreService.js b/dom/datastore/DataStoreService.js index c75766f7edd..523f5fb53aa 100644 --- a/dom/datastore/DataStoreService.js +++ b/dom/datastore/DataStoreService.js @@ -17,6 +17,7 @@ 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/DataStoreDB.jsm"); Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); XPCOMUtils.defineLazyServiceGetter(this, "cpmm", @@ -27,37 +28,54 @@ XPCOMUtils.defineLazyServiceGetter(this, "ppmm", "@mozilla.org/parentprocessmessagemanager;1", "nsIMessageBroadcaster"); +XPCOMUtils.defineLazyServiceGetter(this, "permissionManager", + "@mozilla.org/permissionmanager;1", + "nsIPermissionManager"); + +XPCOMUtils.defineLazyServiceGetter(this, "secMan", + "@mozilla.org/scriptsecuritymanager;1", + "nsIScriptSecurityManager"); + /* DataStoreService */ const DATASTORESERVICE_CID = Components.ID('{d193d0e2-c677-4a7b-bb0a-19155b470f2e}'); +const REVISION_VOID = "void"; function DataStoreService() { debug('DataStoreService Constructor'); - let obs = Services.obs; - if (!obs) { - debug("DataStore Error: observer-service is null!"); - return; + this.inParent = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime) + .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT; + + if (this.inParent) { + let obs = Services.obs; + if (!obs) { + debug("DataStore Error: observer-service is null!"); + return; + } + + obs.addObserver(this, 'webapps-clear-data', false); } - 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); - } + let self = this; + cpmm.addMessageListener("datastore-first-revision-created", + function(aMsg) { self.receiveMessage(aMsg); }); } DataStoreService.prototype = { + inParent: false, + // Hash of DataStores stores: {}, accessStores: {}, + pendingRequests: {}, - installDataStore: function(aAppId, aName, aOwner, aReadOnly) { + installDataStore: function(aAppId, aName, aOrigin, aOwner, aReadOnly) { debug('installDataStore - appId: ' + aAppId + ', aName: ' + - aName + ', aOwner:' + aOwner + ', aReadOnly: ' + - aReadOnly); + aName + ', aOrigin: ' + aOrigin + ', aOwner:' + aOwner + + ', aReadOnly: ' + aReadOnly); + + this.checkIfInParent(); if (aName in this.stores && aAppId in this.stores[aName]) { debug('This should not happen'); @@ -68,13 +86,21 @@ DataStoreService.prototype = { this.stores[aName] = {}; } - this.stores[aName][aAppId] = { owner: aOwner, readOnly: aReadOnly }; + // A DataStore is enabled when it has a first valid revision. + this.stores[aName][aAppId] = { origin: aOrigin, owner: aOwner, + readOnly: aReadOnly, enabled: false }; + + this.addPermissions(aAppId, aName, aOrigin, aOwner, aReadOnly); + + this.createFirstRevisionId(aAppId, aName, aOwner); }, - installAccessDataStore: function(aAppId, aName, aOwner, aReadOnly) { - debug('installDataStore - appId: ' + aAppId + ', aName: ' + - aName + ', aOwner:' + aOwner + ', aReadOnly: ' + - aReadOnly); + installAccessDataStore: function(aAppId, aName, aOrigin, aOwner, aReadOnly) { + debug('installAccessDataStore - appId: ' + aAppId + ', aName: ' + + aName + ', aOrigin: ' + aOrigin + ', aOwner:' + aOwner + + ', aReadOnly: ' + aReadOnly); + + this.checkIfInParent(); if (aName in this.accessStores && aAppId in this.accessStores[aName]) { debug('This should not happen'); @@ -85,56 +111,231 @@ DataStoreService.prototype = { this.accessStores[aName] = {}; } - this.accessStores[aName][aAppId] = { owner: aOwner, readOnly: aReadOnly }; + this.accessStores[aName][aAppId] = { origin: aOrigin, owner: aOwner, + readOnly: aReadOnly }; + this.addAccessPermissions(aAppId, aName, aOrigin, aOwner, aReadOnly); + }, + + checkIfInParent: function() { + if (!this.inParent) { + throw "DataStore can execute this operation just in the parent process"; + } + }, + + createFirstRevisionId: function(aAppId, aName, aOwner) { + debug("createFirstRevisionId database: " + aName); + + let self = this; + let db = new DataStoreDB(); + db.init(aOwner, aName); + db.revisionTxn( + 'readwrite', + function(aTxn, aRevisionStore) { + debug("createFirstRevisionId - 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. + db.addRevision(aRevisionStore, 0, REVISION_VOID, function() { + debug("First revision created."); + if (aName in self.stores && aAppId in self.stores[aName]) { + self.stores[aName][aAppId].enabled = true; + ppmm.broadcastAsyncMessage('datastore-first-revision-created', + { name: aName, owner: aOwner }); + } + }); + } + }; + } + ); + }, + + addPermissions: function(aAppId, aName, aOrigin, aOwner, aReadOnly) { + // When a new DataStore is installed, the permissions must be set for the + // owner app. + let permission = "indexedDB-chrome-" + aName + '|' + aOwner; + this.resetPermissions(aAppId, aOrigin, aOwner, permission, aReadOnly); + + // For any app that wants to have access to this DataStore we add the + // permissions. + if (aName in this.accessStores) { + for (let appId in this.accessStores[aName]) { + // ReadOnly is decided by the owner first. + let readOnly = aReadOnly || this.accessStores[aName][appId].readOnly; + this.resetPermissions(appId, this.accessStores[aName][appId].origin, + this.accessStores[aName][appId].owner, + permission, readOnly); + } + } + }, + + addAccessPermissions: function(aAppId, aName, aOrigin, aOwner, aReadOnly) { + // When an app wants to have access to a DataStore, the permissions must be + // set. + if (!(aName in this.stores)) { + return; + } + + for (let appId in this.stores[aName]) { + let permission = "indexedDB-chrome-" + aName + '|' + this.stores[aName][appId].owner; + // The ReadOnly is decied by the owenr first. + let readOnly = this.stores[aName][appId].readOnly || aReadOnly; + this.resetPermissions(aAppId, aOrigin, aOwner, permission, readOnly); + } + }, + + resetPermissions: function(aAppId, aOrigin, aOwner, aPermission, aReadOnly) { + debug("ResetPermissions - appId: " + aAppId + " - origin: " + aOrigin + + " - owner: " + aOwner + " - permissions: " + aPermission + + " - readOnly: " + aReadOnly); + + let uri = Services.io.newURI(aOrigin, null, null); + let principal = secMan.getAppCodebasePrincipal(uri, aAppId, false); + + let result = permissionManager.testExactPermissionFromPrincipal(principal, + aPermission + '-write'); + + if (aReadOnly && result == Ci.nsIPermissionManager.ALLOW_ACTION) { + debug("Write permission removed"); + permissionManager.removeFromPrincipal(principal, aPermission + '-write'); + } else if (!aReadOnly && result != Ci.nsIPermissionManager.ALLOW_ACTION) { + debug("Write permission added"); + permissionManager.addFromPrincipal(principal, aPermission + '-write', + Ci.nsIPermissionManager.ALLOW_ACTION); + } + + result = permissionManager.testExactPermissionFromPrincipal(principal, + aPermission + '-read'); + if (result != Ci.nsIPermissionManager.ALLOW_ACTION) { + debug("Read permission added"); + permissionManager.addFromPrincipal(principal, aPermission + '-read', + Ci.nsIPermissionManager.ALLOW_ACTION); + } + + result = permissionManager.testExactPermissionFromPrincipal(principal, aPermission); + if (result != Ci.nsIPermissionManager.ALLOW_ACTION) { + debug("Generic permission added"); + permissionManager.addFromPrincipal(principal, aPermission, + Ci.nsIPermissionManager.ALLOW_ACTION); + } }, getDataStores: function(aWindow, aName) { debug('getDataStores - aName: ' + aName); - // This method can be called in the child so we need to send a request to - // the parent and create DataStore object here. - + let self = this; return new aWindow.Promise(function(resolve, reject) { - new DataStoreServiceChild(aWindow, aName, resolve); + // If this request comes from the main process, we have access to the + // window, so we can skip the ipc communication. + if (self.inParent) { + let stores = self.getDataStoresInfo(aName, aWindow.document.nodePrincipal.appId); + self.getDataStoreCreate(aWindow, resolve, stores); + } else { + // This method can be called in the child so we need to send a request + // to the parent and create DataStore object here. + new DataStoreServiceChild(aWindow, aName, function(aStores) { + debug("DataStoreServiceChild callback!"); + self.getDataStoreCreate(aWindow, resolve, aStores); + }); + } }); }, - 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; + getDataStoresInfo: function(aName, aAppId) { + debug('GetDataStoresInfo'); 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 }); + if (aName in this.stores) { + if (aAppId in this.stores[aName]) { + results.push({ name: aName, + owner: this.stores[aName][aAppId].owner, + readOnly: false, + enabled: this.stores[aName][aAppId].enabled }); } - for (var i in this.stores[msg.name]) { - if (i == appId) { + for (var i in this.stores[aName]) { + if (i == aAppId) { continue; } - let access = this.getDataStoreAccess(msg.name, appId); + let access = this.getDataStoreAccess(aName, aAppId); if (!access) { continue; } - let readOnly = this.stores[msg.name][i].readOnly || access.readOnly; - results.push({ store: this.stores[msg.name][i], - readOnly: readOnly }); + let readOnly = this.stores[aName][i].readOnly || access.readOnly; + results.push({ name: aName, + owner: this.stores[aName][i].owner, + readOnly: readOnly, + enabled: this.stores[aName][i].enabled }); } } - msg.stores = results; - aMessage.target.sendAsyncMessage("DataStore:Get:Return", msg); + return results; + }, + + getDataStoreCreate: function(aWindow, aResolve, aStores) { + debug("GetDataStoreCreate"); + + let results = []; + + if (!aStores.length) { + aResolve(results); + return; + } + + let pendingDataStores = []; + + for (let i = 0; i < aStores.length; ++i) { + if (!aStores[i].enabled) { + pendingDataStores.push(aStores[i].owner); + } + } + + if (!pendingDataStores.length) { + this.getDataStoreResolve(aWindow, aResolve, aStores); + return; + } + + if (!(aStores[0].name in this.pendingRequests)) { + this.pendingRequests[aStores[0].name] = []; + } + + this.pendingRequests[aStores[0].name].push({ window: aWindow, + resolve: aResolve, + stores: aStores, + pendingDataStores: pendingDataStores }); + }, + + getDataStoreResolve: function(aWindow, aResolve, aStores) { + debug("GetDataStoreResolve"); + + let callbackPending = aStores.length; + let results = []; + + if (!callbackPending) { + aResolve(results); + return; + } + + for (let i = 0; i < aStores.length; ++i) { + let obj = new DataStore(aWindow, aStores[i].name, + aStores[i].owner, aStores[i].readOnly); + let exposedObj = aWindow.DataStore._create(aWindow, obj); + results.push(exposedObj); + + obj.retrieveRevisionId( + function() { + --callbackPending; + if (!callbackPending) { + aResolve(results); + } + } + ); + } }, getDataStoreAccess: function(aName, aAppId) { @@ -162,6 +363,7 @@ DataStoreService.prototype = { for (let key in this.stores) { if (params.appId in this.stores[key]) { + this.deleteDatabase(key, this.stores[key][params.appId].owner); delete this.stores[key][params.appId]; } @@ -171,6 +373,43 @@ DataStoreService.prototype = { } }, + deleteDatabase: function(aName, aOwner) { + debug("delete database: " + aName); + + let db = new DataStoreDB(); + db.init(aOwner, aName); + db.delete(); + }, + + receiveMessage: function(aMsg) { + debug("receiveMessage"); + let data = aMsg.json; + + if (!(data.name in this.pendingRequests)) { + return; + } + + for (let i = 0; i < this.pendingRequests[data.name].length;) { + let pos = this.pendingRequests[data.name][i].pendingDataStores.indexOf(data.owner); + if (pos != -1) { + this.pendingRequests[data.name][i].pendingDataStores.splice(pos, 1); + if (!this.pendingRequests[data.name][i].pendingDataStores.length) { + this.getDataStoreResolve(this.pendingRequests[data.name][i].window, + this.pendingRequests[data.name][i].resolve, + this.pendingRequests[data.name][i].stores); + this.pendingRequests[data.name].splice(i, 1); + continue; + } + } + + ++i; + } + + if (!this.pendingRequests[data.name].length) { + delete this.pendingRequests[data.name]; + } + }, + classID : DATASTORESERVICE_CID, QueryInterface: XPCOMUtils.generateQI([Ci.nsIDataStoreService, Ci.nsIObserver]), @@ -184,18 +423,17 @@ DataStoreService.prototype = { /* DataStoreServiceChild */ -function DataStoreServiceChild(aWindow, aName, aResolve) { +function DataStoreServiceChild(aWindow, aName, aCallback) { debug("DataStoreServiceChild created"); - this.init(aWindow, aName, aResolve); + this.init(aWindow, aName, aCallback); } DataStoreServiceChild.prototype = { __proto__: DOMRequestIpcHelper.prototype, - init: function(aWindow, aName, aResolve) { - this._window = aWindow; - this._name = aName; - this._resolve = aResolve; + init: function(aWindow, aName, aCallback) { + debug("DataStoreServiceChild init"); + this._callback = aCallback; this.initDOMRequestHelper(aWindow, [ "DataStore:Get:Return" ]); @@ -205,35 +443,12 @@ DataStoreServiceChild.prototype = { }, receiveMessage: function(aMessage) { + debug("DataStoreServiceChild receiveMessage"); 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._callback(aMessage.data.stores); } } diff --git a/dom/datastore/DataStoreServiceInternal.jsm b/dom/datastore/DataStoreServiceInternal.jsm new file mode 100644 index 00000000000..8bafc84c70b --- /dev/null +++ b/dom/datastore/DataStoreServiceInternal.jsm @@ -0,0 +1,51 @@ +/* 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" + +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + +this.EXPORTED_SYMBOLS = ["DataStoreServiceInternal"]; + +function debug(s) { + // dump('DEBUG DataStoreServiceInternal: ' + s + '\n'); +} + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyServiceGetter(this, "ppmm", + "@mozilla.org/parentprocessmessagemanager;1", + "nsIMessageBroadcaster"); + +XPCOMUtils.defineLazyServiceGetter(this, "dataStoreService", + "@mozilla.org/datastore-service;1", + "nsIDataStoreService"); + +this.DataStoreServiceInternal = { + init: function() { + debug("init"); + + 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); + } + }, + + receiveMessage: function(aMessage) { + debug("receiveMessage"); + + if (aMessage.name != 'DataStore:Get') { + return; + } + + let msg = aMessage.data; + + // This is a security issue and it will be fixed by Bug 916091 + msg.stores = dataStoreService.getDataStoresInfo(msg.name, msg.appId); + aMessage.target.sendAsyncMessage("DataStore:Get:Return", msg); + } +} + +DataStoreServiceInternal.init(); diff --git a/dom/datastore/moz.build b/dom/datastore/moz.build index 09efc251f4d..34f08b4142d 100644 --- a/dom/datastore/moz.build +++ b/dom/datastore/moz.build @@ -23,4 +23,5 @@ EXTRA_JS_MODULES += [ 'DataStore.jsm', 'DataStoreChangeNotifier.jsm', 'DataStoreDB.jsm', + 'DataStoreServiceInternal.jsm', ] diff --git a/dom/datastore/nsIDataStoreService.idl b/dom/datastore/nsIDataStoreService.idl index ab9ba87b2da..a3fc914d052 100644 --- a/dom/datastore/nsIDataStoreService.idl +++ b/dom/datastore/nsIDataStoreService.idl @@ -7,19 +7,29 @@ interface nsIDOMWindow; -[scriptable, uuid(d193d0e2-c677-4a7b-bb0a-19155b470f2e)] +[scriptable, uuid(bd02d09c-41ab-47b7-9319-57aa8e5059b0)] interface nsIDataStoreService : nsISupports { void installDataStore(in unsigned long appId, in DOMString name, + in DOMString originURL, in DOMString manifestURL, in boolean readOnly); void installAccessDataStore(in unsigned long appId, in DOMString name, + in DOMString originURL, in DOMString manifestURL, in boolean readOnly); nsISupports getDataStores(in nsIDOMWindow window, in DOMString name); + + // This is an array of objects composed by: + // - readOnly: boolean + // - name: DOMString + // - owner: DOMString + // - enabled: true/false - true if this dataStore is ready to be used. + jsval getDataStoresInfo(in DOMString name, + in unsigned long appId); }; diff --git a/dom/datastore/tests/test_app_install.html b/dom/datastore/tests/test_app_install.html index 6485ad617e7..a51b78854ca 100644 --- a/dom/datastore/tests/test_app_install.html +++ b/dom/datastore/tests/test_app_install.html @@ -10,7 +10,9 @@
diff --git a/dom/datastore/tests/test_basic.html b/dom/datastore/tests/test_basic.html index 2530d9a7e4d..c7ab9f17f38 100644 --- a/dom/datastore/tests/test_basic.html +++ b/dom/datastore/tests/test_basic.html @@ -84,6 +84,7 @@ }, function() { + SpecialPowers.setAllAppsLaunchable(true); SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); runTest(); }, @@ -117,7 +118,10 @@ SimpleTest.finish(); } - SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + if (SpecialPowers.isMainProcess()) { + SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + } + SimpleTest.waitForExplicitFinish(); runTest(); diff --git a/dom/datastore/tests/test_changes.html b/dom/datastore/tests/test_changes.html index d1ff2f25530..138c3499cc2 100644 --- a/dom/datastore/tests/test_changes.html +++ b/dom/datastore/tests/test_changes.html @@ -129,6 +129,7 @@ // Enabling mozBrowser function() { + SpecialPowers.setAllAppsLaunchable(true); SpecialPowers.pushPrefEnv({"set": [["dom.mozBrowserFramesEnabled", true]]}, runTest); }, @@ -170,7 +171,10 @@ SimpleTest.finish(); } - SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + if (SpecialPowers.isMainProcess()) { + SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + } + SimpleTest.waitForExplicitFinish(); runTest(); diff --git a/dom/datastore/tests/test_oop.html b/dom/datastore/tests/test_oop.html index a8fd07cdbef..f09cdf64a3f 100644 --- a/dom/datastore/tests/test_oop.html +++ b/dom/datastore/tests/test_oop.html @@ -88,6 +88,7 @@ }, function() { + SpecialPowers.setAllAppsLaunchable(true); SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); runTest(); }, @@ -121,7 +122,10 @@ SimpleTest.finish(); } - SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + if (SpecialPowers.isMainProcess()) { + SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + } + SimpleTest.waitForExplicitFinish(); runTest(); diff --git a/dom/datastore/tests/test_readonly.html b/dom/datastore/tests/test_readonly.html index 476a5fd9c51..ce524de571e 100644 --- a/dom/datastore/tests/test_readonly.html +++ b/dom/datastore/tests/test_readonly.html @@ -30,6 +30,7 @@ } function runTest() { + SpecialPowers.setAllAppsLaunchable(true); SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); SpecialPowers.autoConfirmAppInstall(continueTest); @@ -94,11 +95,13 @@ SimpleTest.finish(); } - SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + if (SpecialPowers.isMainProcess()) { + SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + } + SimpleTest.waitForExplicitFinish(); - SpecialPowers.pushPrefEnv({"set": [["dom.promise.enabled", true]]}, function() { - SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true]]}, runTest); - }); + SpecialPowers.pushPrefEnv({"set": [["dom.promise.enabled", true], + ["dom.datastore.enabled", true]]}, runTest); diff --git a/dom/datastore/tests/test_revision.html b/dom/datastore/tests/test_revision.html index d14cde5ff90..23846c8469b 100644 --- a/dom/datastore/tests/test_revision.html +++ b/dom/datastore/tests/test_revision.html @@ -94,6 +94,7 @@ // Enabling mozBrowser function() { + SpecialPowers.setAllAppsLaunchable(true); SpecialPowers.pushPrefEnv({"set": [["dom.mozBrowserFramesEnabled", true]]}, runTest); }, @@ -126,7 +127,10 @@ SimpleTest.finish(); } - SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + if (SpecialPowers.isMainProcess()) { + SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); + } + SimpleTest.waitForExplicitFinish(); runTest(); diff --git a/testing/mochitest/manifest.webapp b/testing/mochitest/manifest.webapp index c68b6499bad..e57d77369c0 100644 --- a/testing/mochitest/manifest.webapp +++ b/testing/mochitest/manifest.webapp @@ -35,5 +35,9 @@ "description": "Mochitests" } }, + "datastores-access" : { + "foo" : { "readonly": false }, + "bar" : { "readonly": false } + }, "default_locale": "en-US" }