Bug 871445 - patch 10 - DataStore: indexedDB permissions, r=ehsan

This commit is contained in:
Andrea Marchesini 2013-10-02 13:27:23 -04:00
parent 462224589b
commit 376ecbb43f
16 changed files with 427 additions and 116 deletions

View File

@ -284,6 +284,7 @@ this.DOMApplicationRegistry = {
// Create or Update the DataStore for this app // Create or Update the DataStore for this app
this._readManifests([{ id: aId }], (function(aResult) { this._readManifests([{ id: aId }], (function(aResult) {
this.updateDataStore(this.webapps[aId].localId, this.updateDataStore(this.webapps[aId].localId,
this.webapps[aId].origin,
this.webapps[aId].manifestURL, this.webapps[aId].manifestURL,
aResult[0].manifest); aResult[0].manifest);
}).bind(this)); }).bind(this));
@ -530,12 +531,17 @@ this.DOMApplicationRegistry = {
} }
this.updateOfflineCacheForApp(id); this.updateOfflineCacheForApp(id);
this.updatePermissionsForApp(id); this.updatePermissionsForApp(id);
this.updateDataStoreForApp(id);
} }
// Need to update the persisted list of apps since // Need to update the persisted list of apps since
// installPreinstalledApp() removes the ones failing to install. // installPreinstalledApp() removes the ones failing to install.
this._saveApps(); this._saveApps();
} }
// DataStores must be initialized at startup.
for (let id in this.webapps) {
this.updateDataStoreForApp(id);
}
this.registerAppsHandlers(runUpdate); this.registerAppsHandlers(runUpdate);
}).bind(this); }).bind(this);
@ -552,14 +558,15 @@ this.DOMApplicationRegistry = {
}).bind(this)); }).bind(this));
}, },
updateDataStore: function(aId, aManifestURL, aManifest) { updateDataStore: function(aId, aOrigin, aManifestURL, aManifest) {
if ('datastores-owned' in aManifest) { if ('datastores-owned' in aManifest) {
for (let name in aManifest['datastores-owned']) { for (let name in aManifest['datastores-owned']) {
let readonly = "access" in aManifest['datastores-owned'][name] let readonly = "access" in aManifest['datastores-owned'][name]
? aManifest['datastores-owned'][name].access == 'readonly' ? aManifest['datastores-owned'][name].access == 'readonly'
: false; : 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 !aManifest['datastores-access'][name].readonly
? false : true; ? false : true;
// The first release is always in readonly mode. dataStoreService.installAccessDataStore(aId, name, aOrigin,
dataStoreService.installAccessDataStore(aId, name, aManifestURL, aManifestURL, readonly);
/* readonly */ true);
} }
} }
}, },
@ -1434,7 +1440,8 @@ this.DOMApplicationRegistry = {
manifestURL: app.manifestURL }, manifestURL: app.manifestURL },
true); 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", this.broadcastMessage("Webapps:PackageEvent",
{ type: "applied", { type: "applied",
manifestURL: app.manifestURL, manifestURL: app.manifestURL,
@ -1640,7 +1647,8 @@ this.DOMApplicationRegistry = {
}, true); }, 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.name = manifest.name;
app.csp = manifest.csp || ""; app.csp = manifest.csp || "";
@ -2142,8 +2150,8 @@ this.DOMApplicationRegistry = {
true); true);
} }
this.updateDataStore(this.webapps[aId].localId, appObject.manifestURL, this.updateDataStore(this.webapps[aId].localId, appObject.origin,
aManifest); appObject.manifestURL, aManifest);
debug("About to fire Webapps:PackageEvent 'installed'"); debug("About to fire Webapps:PackageEvent 'installed'");
this.broadcastMessage("Webapps:PackageEvent", this.broadcastMessage("Webapps:PackageEvent",
@ -2248,8 +2256,8 @@ this.DOMApplicationRegistry = {
}).bind(this)); }).bind(this));
} }
this.updateDataStore(this.webapps[id].localId, this.webapps[id].manifestURL, this.updateDataStore(this.webapps[id].localId, this.webapps[id].origin,
jsonManifest); this.webapps[id].manifestURL, jsonManifest);
} }
["installState", "downloadAvailable", ["installState", "downloadAvailable",

View File

@ -103,7 +103,7 @@ this.DataStore.prototype = {
let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data; let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
if (wId == self._innerWindowID) { if (wId == self._innerWindowID) {
cpmm.removeMessageListener("DataStore:Changed:Return:OK", self); cpmm.removeMessageListener("DataStore:Changed:Return:OK", self);
self._db.delete(); self._db.close();
} }
}, "inner-window-destroyed", false); }, "inner-window-destroyed", false);
@ -278,26 +278,18 @@ this.DataStore.prototype = {
retrieveRevisionId: function(aSuccessCb) { retrieveRevisionId: function(aSuccessCb) {
let self = this; let self = this;
this._db.revisionTxn( this._db.revisionTxn(
'readwrite', 'readonly',
function(aTxn, aRevisionStore) { function(aTxn, aRevisionStore) {
debug("RetrieveRevisionId transaction success"); debug("RetrieveRevisionId transaction success");
let request = aRevisionStore.openCursor(null, 'prev'); let request = aRevisionStore.openCursor(null, 'prev');
request.onsuccess = function(aEvent) { request.onsuccess = function(aEvent) {
let cursor = aEvent.target.result; let cursor = aEvent.target.result;
if (!cursor) { if (cursor) {
// If the revision doesn't exist, let's create the first one. self._revisionId = cursor.value.revisionId;
self.addRevision(aRevisionStore, 0, REVISION_VOID,
function(aRevisionId) {
self._revisionId = aRevisionId;
aSuccessCb();
}
);
return;
} }
self._revisionId = cursor.value.revisionId; aSuccessCb(self._revisionId);
aSuccessCb();
}; };
} }
); );
@ -475,7 +467,7 @@ this.DataStore.prototype = {
removedIds: {} removedIds: {}
}; };
let request = aStore.mozGetAll(self._window.IDBKeyRange.lowerBound(aInternalRevisionId, true)); let request = aStore.mozGetAll(IDBKeyRange.lowerBound(aInternalRevisionId, true));
request.onsuccess = function(aEvent) { request.onsuccess = function(aEvent) {
for (let i = 0; i < aEvent.target.result.length; ++i) { for (let i = 0; i < aEvent.target.result.length; ++i) {
let data = aEvent.target.result[i]; let data = aEvent.target.result[i];

View File

@ -12,6 +12,9 @@ function debug(s) {
//dump('DEBUG DataStoreChangeNotifier: ' + s + '\n'); //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/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");

View File

@ -39,8 +39,8 @@ DataStoreDB.prototype = {
store.createIndex(DATASTOREDB_REVISION_INDEX, 'revisionId', { unique: true }); store.createIndex(DATASTOREDB_REVISION_INDEX, 'revisionId', { unique: true });
}, },
init: function(aOrigin, aName) { init: function(aOwner, aName) {
let dbName = aOrigin + '_' + aName; let dbName = aName + '|' + aOwner;
this.initDBHelper(dbName, DATASTOREDB_VERSION, this.initDBHelper(dbName, DATASTOREDB_VERSION,
[DATASTOREDB_OBJECTSTORE_NAME, DATASTOREDB_REVISION]); [DATASTOREDB_OBJECTSTORE_NAME, DATASTOREDB_REVISION]);
}, },

View File

@ -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/XPCOMUtils.jsm');
Cu.import('resource://gre/modules/Services.jsm'); Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/DataStore.jsm'); Cu.import('resource://gre/modules/DataStore.jsm');
Cu.import("resource://gre/modules/DataStoreDB.jsm");
Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "cpmm", XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
@ -27,37 +28,54 @@ XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
"@mozilla.org/parentprocessmessagemanager;1", "@mozilla.org/parentprocessmessagemanager;1",
"nsIMessageBroadcaster"); "nsIMessageBroadcaster");
XPCOMUtils.defineLazyServiceGetter(this, "permissionManager",
"@mozilla.org/permissionmanager;1",
"nsIPermissionManager");
XPCOMUtils.defineLazyServiceGetter(this, "secMan",
"@mozilla.org/scriptsecuritymanager;1",
"nsIScriptSecurityManager");
/* DataStoreService */ /* DataStoreService */
const DATASTORESERVICE_CID = Components.ID('{d193d0e2-c677-4a7b-bb0a-19155b470f2e}'); const DATASTORESERVICE_CID = Components.ID('{d193d0e2-c677-4a7b-bb0a-19155b470f2e}');
const REVISION_VOID = "void";
function DataStoreService() { function DataStoreService() {
debug('DataStoreService Constructor'); debug('DataStoreService Constructor');
let obs = Services.obs; this.inParent = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
if (!obs) { .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
debug("DataStore Error: observer-service is null!");
return; 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 self = this;
cpmm.addMessageListener("datastore-first-revision-created",
let inParent = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime) function(aMsg) { self.receiveMessage(aMsg); });
.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
if (inParent) {
ppmm.addMessageListener("DataStore:Get", this);
}
} }
DataStoreService.prototype = { DataStoreService.prototype = {
inParent: false,
// Hash of DataStores // Hash of DataStores
stores: {}, stores: {},
accessStores: {}, accessStores: {},
pendingRequests: {},
installDataStore: function(aAppId, aName, aOwner, aReadOnly) { installDataStore: function(aAppId, aName, aOrigin, aOwner, aReadOnly) {
debug('installDataStore - appId: ' + aAppId + ', aName: ' + debug('installDataStore - appId: ' + aAppId + ', aName: ' +
aName + ', aOwner:' + aOwner + ', aReadOnly: ' + aName + ', aOrigin: ' + aOrigin + ', aOwner:' + aOwner +
aReadOnly); ', aReadOnly: ' + aReadOnly);
this.checkIfInParent();
if (aName in this.stores && aAppId in this.stores[aName]) { if (aName in this.stores && aAppId in this.stores[aName]) {
debug('This should not happen'); debug('This should not happen');
@ -68,13 +86,21 @@ DataStoreService.prototype = {
this.stores[aName] = {}; 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) { installAccessDataStore: function(aAppId, aName, aOrigin, aOwner, aReadOnly) {
debug('installDataStore - appId: ' + aAppId + ', aName: ' + debug('installAccessDataStore - appId: ' + aAppId + ', aName: ' +
aName + ', aOwner:' + aOwner + ', aReadOnly: ' + aName + ', aOrigin: ' + aOrigin + ', aOwner:' + aOwner +
aReadOnly); ', aReadOnly: ' + aReadOnly);
this.checkIfInParent();
if (aName in this.accessStores && aAppId in this.accessStores[aName]) { if (aName in this.accessStores && aAppId in this.accessStores[aName]) {
debug('This should not happen'); debug('This should not happen');
@ -85,56 +111,231 @@ DataStoreService.prototype = {
this.accessStores[aName] = {}; 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) { getDataStores: function(aWindow, aName) {
debug('getDataStores - aName: ' + aName); debug('getDataStores - aName: ' + aName);
// This method can be called in the child so we need to send a request to let self = this;
// the parent and create DataStore object here.
return new aWindow.Promise(function(resolve, reject) { 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) { getDataStoresInfo: function(aName, aAppId) {
if (aMessage.name != 'DataStore:Get') { debug('GetDataStoresInfo');
return;
}
let msg = aMessage.data;
// This is a security issue and it will be fixed by Bug 916091
let appId = msg.appId;
let results = []; let results = [];
if (msg.name in this.stores) { if (aName in this.stores) {
if (appId in this.stores[msg.name]) { if (aAppId in this.stores[aName]) {
results.push({ store: this.stores[msg.name][appId], results.push({ name: aName,
readOnly: false }); owner: this.stores[aName][aAppId].owner,
readOnly: false,
enabled: this.stores[aName][aAppId].enabled });
} }
for (var i in this.stores[msg.name]) { for (var i in this.stores[aName]) {
if (i == appId) { if (i == aAppId) {
continue; continue;
} }
let access = this.getDataStoreAccess(msg.name, appId); let access = this.getDataStoreAccess(aName, aAppId);
if (!access) { if (!access) {
continue; continue;
} }
let readOnly = this.stores[msg.name][i].readOnly || access.readOnly; let readOnly = this.stores[aName][i].readOnly || access.readOnly;
results.push({ store: this.stores[msg.name][i], results.push({ name: aName,
readOnly: readOnly }); owner: this.stores[aName][i].owner,
readOnly: readOnly,
enabled: this.stores[aName][i].enabled });
} }
} }
msg.stores = results; return results;
aMessage.target.sendAsyncMessage("DataStore:Get:Return", msg); },
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) { getDataStoreAccess: function(aName, aAppId) {
@ -162,6 +363,7 @@ DataStoreService.prototype = {
for (let key in this.stores) { for (let key in this.stores) {
if (params.appId in this.stores[key]) { if (params.appId in this.stores[key]) {
this.deleteDatabase(key, this.stores[key][params.appId].owner);
delete this.stores[key][params.appId]; 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, classID : DATASTORESERVICE_CID,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDataStoreService, QueryInterface: XPCOMUtils.generateQI([Ci.nsIDataStoreService,
Ci.nsIObserver]), Ci.nsIObserver]),
@ -184,18 +423,17 @@ DataStoreService.prototype = {
/* DataStoreServiceChild */ /* DataStoreServiceChild */
function DataStoreServiceChild(aWindow, aName, aResolve) { function DataStoreServiceChild(aWindow, aName, aCallback) {
debug("DataStoreServiceChild created"); debug("DataStoreServiceChild created");
this.init(aWindow, aName, aResolve); this.init(aWindow, aName, aCallback);
} }
DataStoreServiceChild.prototype = { DataStoreServiceChild.prototype = {
__proto__: DOMRequestIpcHelper.prototype, __proto__: DOMRequestIpcHelper.prototype,
init: function(aWindow, aName, aResolve) { init: function(aWindow, aName, aCallback) {
this._window = aWindow; debug("DataStoreServiceChild init");
this._name = aName; this._callback = aCallback;
this._resolve = aResolve;
this.initDOMRequestHelper(aWindow, [ "DataStore:Get:Return" ]); this.initDOMRequestHelper(aWindow, [ "DataStore:Get:Return" ]);
@ -205,35 +443,12 @@ DataStoreServiceChild.prototype = {
}, },
receiveMessage: function(aMessage) { receiveMessage: function(aMessage) {
debug("DataStoreServiceChild receiveMessage");
if (aMessage.name != 'DataStore:Get:Return') { if (aMessage.name != 'DataStore:Get:Return') {
return; return;
} }
let msg = aMessage.data;
let self = this;
let callbackPending = msg.stores.length; this._callback(aMessage.data.stores);
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);
}
}
);
}
} }
} }

View File

@ -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();

View File

@ -23,4 +23,5 @@ EXTRA_JS_MODULES += [
'DataStore.jsm', 'DataStore.jsm',
'DataStoreChangeNotifier.jsm', 'DataStoreChangeNotifier.jsm',
'DataStoreDB.jsm', 'DataStoreDB.jsm',
'DataStoreServiceInternal.jsm',
] ]

View File

@ -7,19 +7,29 @@
interface nsIDOMWindow; interface nsIDOMWindow;
[scriptable, uuid(d193d0e2-c677-4a7b-bb0a-19155b470f2e)] [scriptable, uuid(bd02d09c-41ab-47b7-9319-57aa8e5059b0)]
interface nsIDataStoreService : nsISupports interface nsIDataStoreService : nsISupports
{ {
void installDataStore(in unsigned long appId, void installDataStore(in unsigned long appId,
in DOMString name, in DOMString name,
in DOMString originURL,
in DOMString manifestURL, in DOMString manifestURL,
in boolean readOnly); in boolean readOnly);
void installAccessDataStore(in unsigned long appId, void installAccessDataStore(in unsigned long appId,
in DOMString name, in DOMString name,
in DOMString originURL,
in DOMString manifestURL, in DOMString manifestURL,
in boolean readOnly); in boolean readOnly);
nsISupports getDataStores(in nsIDOMWindow window, nsISupports getDataStores(in nsIDOMWindow window,
in DOMString name); 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);
}; };

View File

@ -10,7 +10,9 @@
<div id="container"></div> <div id="container"></div>
<script type="application/javascript;version=1.7"> <script type="application/javascript;version=1.7">
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); if (SpecialPowers.isMainProcess()) {
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
}
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();
@ -23,7 +25,8 @@
{ "type": "embed-apps", "allow": 1, "context": document }, { "type": "embed-apps", "allow": 1, "context": document },
{ "type": "webapps-manage", "allow": 1, "context": document }], { "type": "webapps-manage", "allow": 1, "context": document }],
function() { function() {
SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true]]}, function() { SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true],
["dom.promise.enabled", true]]}, function() {
gGenerator.next(); }); gGenerator.next(); });
}); });
@ -46,6 +49,7 @@
}, cbError); }, cbError);
yield undefined; yield undefined;
SpecialPowers.setAllAppsLaunchable(true);
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
SpecialPowers.autoConfirmAppInstall(continueTest); SpecialPowers.autoConfirmAppInstall(continueTest);

View File

@ -84,6 +84,7 @@
}, },
function() { function() {
SpecialPowers.setAllAppsLaunchable(true);
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
runTest(); runTest();
}, },
@ -117,7 +118,10 @@
SimpleTest.finish(); SimpleTest.finish();
} }
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); if (SpecialPowers.isMainProcess()) {
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
}
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();
runTest(); runTest();
</script> </script>

View File

@ -84,6 +84,7 @@
}, },
function() { function() {
SpecialPowers.setAllAppsLaunchable(true);
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
runTest(); runTest();
}, },
@ -117,7 +118,10 @@
SimpleTest.finish(); SimpleTest.finish();
} }
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); if (SpecialPowers.isMainProcess()) {
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
}
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();
runTest(); runTest();
</script> </script>

View File

@ -129,6 +129,7 @@
// Enabling mozBrowser // Enabling mozBrowser
function() { function() {
SpecialPowers.setAllAppsLaunchable(true);
SpecialPowers.pushPrefEnv({"set": [["dom.mozBrowserFramesEnabled", true]]}, runTest); SpecialPowers.pushPrefEnv({"set": [["dom.mozBrowserFramesEnabled", true]]}, runTest);
}, },
@ -170,7 +171,10 @@
SimpleTest.finish(); SimpleTest.finish();
} }
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); if (SpecialPowers.isMainProcess()) {
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
}
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();
runTest(); runTest();
</script> </script>

View File

@ -88,6 +88,7 @@
}, },
function() { function() {
SpecialPowers.setAllAppsLaunchable(true);
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
runTest(); runTest();
}, },
@ -121,7 +122,10 @@
SimpleTest.finish(); SimpleTest.finish();
} }
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); if (SpecialPowers.isMainProcess()) {
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
}
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();
runTest(); runTest();
</script> </script>

View File

@ -30,6 +30,7 @@
} }
function runTest() { function runTest() {
SpecialPowers.setAllAppsLaunchable(true);
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
SpecialPowers.autoConfirmAppInstall(continueTest); SpecialPowers.autoConfirmAppInstall(continueTest);
@ -94,11 +95,13 @@
SimpleTest.finish(); SimpleTest.finish();
} }
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); if (SpecialPowers.isMainProcess()) {
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
}
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.promise.enabled", true]]}, function() { SpecialPowers.pushPrefEnv({"set": [["dom.promise.enabled", true],
SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true]]}, runTest); ["dom.datastore.enabled", true]]}, runTest);
});
</script> </script>
</body> </body>
</html> </html>

View File

@ -94,6 +94,7 @@
// Enabling mozBrowser // Enabling mozBrowser
function() { function() {
SpecialPowers.setAllAppsLaunchable(true);
SpecialPowers.pushPrefEnv({"set": [["dom.mozBrowserFramesEnabled", true]]}, runTest); SpecialPowers.pushPrefEnv({"set": [["dom.mozBrowserFramesEnabled", true]]}, runTest);
}, },
@ -126,7 +127,10 @@
SimpleTest.finish(); SimpleTest.finish();
} }
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm"); if (SpecialPowers.isMainProcess()) {
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
}
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();
runTest(); runTest();
</script> </script>

View File

@ -35,5 +35,9 @@
"description": "Mochitests" "description": "Mochitests"
} }
}, },
"datastores-access" : {
"foo" : { "readonly": false },
"bar" : { "readonly": false }
},
"default_locale": "en-US" "default_locale": "en-US"
} }