Bug 871445 - patch 1 - DataStoreService and getDataStores(), r=mounir, r=fabrice, r=ehsan

--HG--
rename : dom/apps/tests/file_app.sjs => dom/datastore/tests/file_app.sjs
rename : dom/apps/tests/file_hosted_app.template.webapp => dom/datastore/tests/file_app.template.webapp
This commit is contained in:
Andrea Marchesini 2013-10-02 13:27:07 -04:00
parent 98e8051ead
commit 583e47bb3f
21 changed files with 502 additions and 2 deletions

View File

@ -18,4 +18,4 @@
# Modifying this file will now automatically clobber the buildbot machines \o/
#
Bug 921563 needs a clobber due to apparent ipdl regeneration bugs.
Bug 871445 needs a clobber.

View File

@ -795,6 +795,8 @@ pref("network.sntp.timeout", 30); // In seconds.
// Enable promise
pref("dom.promise.enabled", false);
pref("dom.datastore.enabled", true);
// DOM Inter-App Communication API.
#ifdef MOZ_WIDGET_GONK
// Enable this only for gonk-specific build but not for desktop build.

View File

@ -766,6 +766,10 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DLL_SUFFIX@
@BINPATH@/components/B2GAboutRedirector.js
@BINPATH@/components/FilePicker.js
@BINPATH@/components/DataStore.manifest
@BINPATH@/components/DataStoreService.js
@BINPATH@/components/dom_datastore.xpt
#ifdef MOZ_WEBSPEECH
@BINPATH@/components/dom_webspeechsynth.xpt
#endif

View File

@ -816,6 +816,11 @@ bin/libfreebl_32int64_3.so
@BINPATH@/metro/modules
#endif
@BINPATH@/components/DataStore.manifest
@BINPATH@/components/DataStoreService.js
@BINPATH@/components/dom_datastore.xpt
#ifdef MOZ_ASAN
@BINPATH@/llvm-symbolizer
#endif

View File

@ -65,6 +65,10 @@ XPCOMUtils.defineLazyGetter(this, "interAppCommService", function() {
.getService(Ci.nsIInterAppCommService);
});
XPCOMUtils.defineLazyServiceGetter(this, "dataStoreService",
"@mozilla.org/datastore-service;1",
"nsIDataStoreService");
XPCOMUtils.defineLazyGetter(this, "msgmgr", function() {
return Cc["@mozilla.org/system-message-internal;1"]
.getService(Ci.nsISystemMessagesInternal);
@ -272,6 +276,19 @@ this.DOMApplicationRegistry = {
}
},
updateDataStoreForApp: function(aId) {
if (!this.webapps[aId]) {
return;
}
// Create or Update the DataStore for this app
this._readManifests([{ id: aId }], (function(aResult) {
this.updateDataStore(this.webapps[aId].localId,
this.webapps[aId].manifestURL,
aResult[0].manifest);
}).bind(this));
},
updatePermissionsForApp: function updatePermissionsForApp(aId) {
if (!this.webapps[aId]) {
return;
@ -513,6 +530,7 @@ 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.
@ -534,6 +552,20 @@ this.DOMApplicationRegistry = {
}).bind(this));
},
updateDataStore: function(aId, aManifestURL, aManifest) {
if (!("datastores" in aManifest)) {
return;
}
for (let name in aManifest.datastores) {
let readonly = ("readonly" in aManifest.datastores[name]) &&
!aManifest.datastores[name].readonly
? false : true;
dataStoreService.installDataStore(aId, name, aManifestURL, readonly);
}
},
// |aEntryPoint| is either the entry_point name or the null in which case we
// use the root of the manifest.
//
@ -1392,7 +1424,7 @@ this.DOMApplicationRegistry = {
manifestURL: app.manifestURL },
true);
}
this.updateDataStore(this.webapps[id].localId, app.manifestURL, aData);
this.broadcastMessage("Webapps:PackageEvent",
{ type: "applied",
manifestURL: app.manifestURL,
@ -1598,6 +1630,8 @@ this.DOMApplicationRegistry = {
}, true);
}
this.updateDataStore(this.webapps[id].localId, app.manifestURL, app.manifest);
app.name = manifest.name;
app.csp = manifest.csp || "";
app.role = manifest.role || "";
@ -2097,6 +2131,10 @@ this.DOMApplicationRegistry = {
manifestURL: appObject.manifestURL },
true);
}
this.updateDataStore(this.webapps[aId].localId, appObject.manifestURL,
aManifest);
debug("About to fire Webapps:PackageEvent 'installed'");
this.broadcastMessage("Webapps:PackageEvent",
{ type: "installed",
@ -2199,6 +2237,9 @@ this.DOMApplicationRegistry = {
this.uninstall(aData, aData.mm);
}).bind(this));
}
this.updateDataStore(this.webapps[id].localId, this.webapps[id].manifestURL,
jsonManifest);
}
["installState", "downloadAvailable",

View File

@ -70,11 +70,13 @@
#endif
#include "nsIDOMGlobalPropertyInitializer.h"
#include "nsIDataStoreService.h"
#include "nsJSUtils.h"
#include "nsScriptNameSpaceManager.h"
#include "mozilla/dom/NavigatorBinding.h"
#include "mozilla/dom/Promise.h"
namespace mozilla {
namespace dom {
@ -1095,6 +1097,28 @@ Navigator::GetBattery(ErrorResult& aRv)
return mBatteryManager;
}
already_AddRefed<Promise>
Navigator::GetDataStores(const nsAString& aName, ErrorResult& aRv)
{
if (!mWindow || !mWindow->GetDocShell()) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
nsCOMPtr<nsIDataStoreService> service =
do_GetService("@mozilla.org/datastore-service;1");
if (!service) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsCOMPtr<nsISupports> promise;
aRv = service->GetDataStores(mWindow, aName, getter_AddRefs(promise));
nsRefPtr<Promise> p = static_cast<Promise*>(promise.get());
return p.forget();
}
PowerManager*
Navigator::GetMozPower(ErrorResult& aRv)
{

View File

@ -56,6 +56,8 @@ class BatteryManager;
class FMRadio;
#endif
class Promise;
class DesktopNotificationCenter;
class MobileMessageManager;
class MozIdleObserver;
@ -175,6 +177,8 @@ public:
// The XPCOM GetDoNotTrack is ok
Geolocation* GetGeolocation(ErrorResult& aRv);
battery::BatteryManager* GetBattery(ErrorResult& aRv);
already_AddRefed<Promise> GetDataStores(const nsAString &aName,
ErrorResult& aRv);
bool Vibrate(uint32_t aDuration);
bool Vibrate(const nsTArray<uint32_t>& aDuration);
void GetAppCodeName(nsString& aAppCodeName, ErrorResult& aRv)

View File

@ -0,0 +1,63 @@
/* -*- 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'
var EXPORTED_SYMBOLS = ["DataStore"];
/* DataStore object */
function DataStore(aAppId, aName, aOwner, aReadOnly) {
this.appId = aAppId;
this.name = aName;
this.owner = aOwner;
this.readOnly = aReadOnly;
}
DataStore.prototype = {
appId: null,
name: null,
owner: null,
readOnly: null,
exposeObject: function(aWindow) {
let self = this;
let chromeObject = {
get name() {
return self.name;
},
get owner() {
return self.owner;
},
get readOnly() {
return self.readOnly;
},
/* TODO:
Promise<Object> get(unsigned long id);
Promise<void> update(unsigned long id, any obj);
Promise<int> add(any obj)
Promise<boolean> remove(unsigned long id)
Promise<void> clear();
readonly attribute DOMString revisionId
attribute EventHandler onchange;
Promise<DataStoreChanges> getChanges(DOMString revisionId)
getAll(), getLength()
*/
__exposedProps__: {
name: 'r',
owner: 'r',
readOnly: 'r'
}
};
return chromeObject;
}
};

View File

@ -0,0 +1,2 @@
component {d193d0e2-c677-4a7b-bb0a-19155b470f2e} DataStoreService.js
contract @mozilla.org/datastore-service;1 {d193d0e2-c677-4a7b-bb0a-19155b470f2e}

View File

@ -0,0 +1,115 @@
/* -*- 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'
/* static functions */
let DEBUG = 0;
let debug;
if (DEBUG)
debug = function (s) { dump('DEBUG DataStore: ' + s + '\n'); }
else
debug = function (s) {}
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');
/* DataStoreService */
const DATASTORESERVICE_CID = Components.ID('{d193d0e2-c677-4a7b-bb0a-19155b470f2e}');
function DataStoreService() {
debug('DataStoreService Constructor');
let obs = Services.obs;
if (!obs) {
debug("DataStore Error: observer-service is null!");
return;
}
obs.addObserver(this, 'webapps-clear-data', false);
}
DataStoreService.prototype = {
// Hash of DataStores
stores: {},
installDataStore: function(aAppId, aName, aOwner, aReadOnly) {
debug('installDataStore - appId: ' + aAppId + ', aName: ' + aName +
', aOwner:' + aOwner + ', aReadOnly: ' + aReadOnly);
if (aName in this.stores && aAppId in this.stores[aName]) {
debug('This should not happen');
return;
}
let store = new DataStore(aAppId, aName, aOwner, aReadOnly);
if (!(aName in this.stores)) {
this.stores[aName] = {};
}
this.stores[aName][aAppId] = store;
},
getDataStores: function(aWindow, aName) {
debug('getDataStores - aName: ' + aName);
let self = this;
return new aWindow.Promise(function(resolve, reject) {
let results = [];
if (aName in self.stores) {
for (let appId in self.stores[aName]) {
let obj = self.stores[aName][appId].exposeObject(aWindow);
results.push(obj);
}
}
resolve(results);
});
},
observe: function observe(aSubject, aTopic, aData) {
debug('getDataStores - aTopic: ' + aTopic);
if (aTopic != 'webapps-clear-data') {
return;
}
let params =
aSubject.QueryInterface(Ci.mozIApplicationClearPrivateDataParams);
// DataStore is explosed to apps, not browser content.
if (params.browserOnly) {
return;
}
for (let key in this.stores) {
if (params.appId in this.stores[key]) {
delete this.stores[key][params.appId];
}
if (!this.stores[key].length) {
delete this.stores[key];
}
}
},
classID : DATASTORESERVICE_CID,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDataStoreService,
Ci.nsIObserver]),
classInfo: XPCOMUtils.generateCI({
classID: DATASTORESERVICE_CID,
contractID: '@mozilla.org/datastore-service;1',
interfaces: [Ci.nsIDataStoreService, Ci.nsIObserver],
flags: Ci.nsIClassInfo.SINGLETON
})
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DataStoreService]);

24
dom/datastore/moz.build Normal file
View File

@ -0,0 +1,24 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
TEST_DIRS += ['tests']
XPIDL_SOURCES += [
'nsIDataStoreService.idl',
]
XPIDL_MODULE = 'dom_datastore'
MODULE = 'dom'
EXTRA_COMPONENTS += [
'DataStore.manifest',
'DataStoreService.js',
]
EXTRA_JS_MODULES += [
'DataStore.jsm',
]

View File

@ -0,0 +1,20 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#include "nsISupports.idl"
interface nsIDOMWindow;
[scriptable, uuid(d193d0e2-c677-4a7b-bb0a-19155b470f2e)]
interface nsIDataStoreService : nsISupports
{
void installDataStore(in unsigned long appId,
in DOMString name,
in DOMString manifestURL,
in boolean readOnly);
nsISupports getDataStores(in nsIDOMWindow window,
in DOMString name);
};

View File

@ -0,0 +1,20 @@
# 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/.
DEPTH = @DEPTH@
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
relativesrcdir = @relativesrcdir@
include $(DEPTH)/config/autoconf.mk
MOCHITEST_FILES = \
test_app_install.html \
file_app.sjs \
file_app.template.webapp \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,37 @@
var gBasePath = "tests/dom/datastore/tests/";
var gAppTemplatePath = "tests/dom/datastore/tests/file_app.template.webapp";
function handleRequest(request, response) {
var template = gBasePath + 'file_app.template.webapp';
response.setHeader("Content-Type", "application/x-web-app-manifest+json", false);
response.write(readTemplate(template));
}
// Copy-pasted incantations. There ought to be a better way to synchronously read
// a file into a string, but I guess we're trying to discourage that.
function readTemplate(path) {
var file = Components.classes["@mozilla.org/file/directory_service;1"].
getService(Components.interfaces.nsIProperties).
get("CurWorkD", Components.interfaces.nsILocalFile);
var fis = Components.classes['@mozilla.org/network/file-input-stream;1'].
createInstance(Components.interfaces.nsIFileInputStream);
var cis = Components.classes["@mozilla.org/intl/converter-input-stream;1"].
createInstance(Components.interfaces.nsIConverterInputStream);
var split = path.split("/");
for(var i = 0; i < split.length; ++i) {
file.append(split[i]);
}
fis.init(file, -1, -1, false);
cis.init(fis, "UTF-8", 0, 0);
var data = "";
let (str = {}) {
let read = 0;
do {
read = cis.readString(0xffffffff, str); // read as much as we can and put it in str.value
data += str.value;
} while (read != 0);
}
cis.close();
return data;
}

View File

@ -0,0 +1,10 @@
{
"name": "Really Rapid Release (hosted)",
"description": "Updated even faster than <a href='http://mozilla.org'>Firefox</a>, just to annoy slashdotters.",
"launch_path": "/tests/dom/datastore/tests/file_app.sjs",
"icons": { "128": "default_icon" },
"datastores" : {
"foo" : { "readonly": false, "description" : "This store is called foo" },
"bar" : { "readonly": true, "description" : "This store is called bar" }
}
}

View File

@ -0,0 +1,5 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.

View File

@ -0,0 +1,105 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test for DataStore - install/uninstall apps</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript;version=1.7">
SimpleTest.waitForExplicitFinish();
var gBaseURL = 'http://test/tests/dom/datastore/tests/';
var gHostedManifestURL = gBaseURL + 'file_app.sjs';
var gGenerator = runTest();
function go() {
SpecialPowers.pushPermissions(
[{ "type": "browser", "allow": 1, "context": document },
{ "type": "embed-apps", "allow": 1, "context": document },
{ "type": "webapps-manage", "allow": 1, "context": document }],
function() { gGenerator.next() });
}
function continueTest() {
gGenerator.next();
}
function cbError() {
ok(false, "Error callback invoked");
finish();
}
function runTest() {
ok("getDataStores" in navigator, "getDataStores exists");
is(typeof navigator.getDataStores, "function", "getDataStores exists and it's a function");
navigator.getDataStores('foo').then(function(stores) {
is(stores.length, 0, "getDataStores('foo') returns 0 elements");
continueTest();
}, cbError);
yield undefined;
SpecialPowers.autoConfirmAppInstall(continueTest);
yield undefined;
var request = navigator.mozApps.install(gHostedManifestURL);
request.onerror = cbError;
request.onsuccess = continueTest;
yield undefined;
var app = request.result;
isnot(app, null, "App is non-null");
is(app.manifest.description, "Updated even faster than Firefox, just to annoy slashdotters.",
"Manifest is HTML-sanitized");
navigator.getDataStores('foo').then(function(stores) {
is(stores.length, 1, "getDataStores('foo') returns 1 element");
is(stores[0].name, 'foo', 'The dataStore.name is foo');
is(stores[0].owner, 'http://test/tests/dom/datastore/tests/file_app.sjs', 'The dataStore.owner exists');
is(stores[0].readOnly, false, 'The dataStore foo is not in readonly');
continueTest();
}, cbError);
yield undefined;
navigator.getDataStores('bar').then(function(stores) {
is(stores.length, 1, "getDataStores('bar') returns 1 element");
is(stores[0].name, 'bar', 'The dataStore.name is bar');
is(stores[0].owner, 'http://test/tests/dom/datastore/tests/file_app.sjs', 'The dataStore.owner exists');
is(stores[0].readOnly, true, 'The dataStore bar is in readonly');
continueTest();
}, cbError);
yield undefined;
// Uninstall the app.
request = navigator.mozApps.mgmt.uninstall(app);
request.onerror = cbError;
request.onsuccess = continueTest;
yield undefined;
navigator.getDataStores('foo').then(function(stores) {
is(stores.length, 0, "getDataStores('foo') returns 0 elements");
continueTest();
}, cbError);
yield undefined;
// All done.
info("All done");
finish();
}
function finish() {
SimpleTest.finish();
}
</script>
</head>
<body onload="go()">
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
<div id="container"></div>
</body>
</html>

View File

@ -46,6 +46,7 @@ PARALLEL_DIRS += [
'contacts',
'phonenumberutils',
'alarm',
'datastore',
'devicestorage',
'encoding',
'file',

View File

@ -108,6 +108,14 @@ interface NavigatorBattery {
};
Navigator implements NavigatorBattery;
// https://wiki.mozilla.org/WebAPI/DataStore
[NoInterfaceObject]
interface NavigatorDataStore {
[Throws, Creator, Pref="dom.datastore.enabled"]
Promise getDataStores(DOMString name);
};
Navigator implements NavigatorDataStore;
// http://www.w3.org/TR/vibration/#vibration-interface
partial interface Navigator {
// We don't support sequences in unions yet

View File

@ -591,3 +591,7 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DLL_SUFFIX@
@BINPATH@/components/MarionetteComponents.manifest
@BINPATH@/components/marionettecomponent.js
#endif
@BINPATH@/components/DataStore.manifest
@BINPATH@/components/DataStoreService.js
@BINPATH@/components/dom_datastore.xpt

View File

@ -4434,6 +4434,12 @@ pref("dom.forms.inputmode", true);
// InputMethods for soft keyboards in B2G
pref("dom.mozInputMethod.enabled", false);
#ifdef RELEASE_BUILD
pref("dom.datastore.enabled", false);
#else
pref("dom.datastore.enabled", true);
#endif
// Telephony API
pref("dom.telephony.enabled", false);