Bug 1176434 - Enabling indexedDB for content JS sandboxes, r=bent

This commit is contained in:
Martin Thomson 2015-07-02 13:30:15 -07:00
parent 742b0e4f95
commit c1cec6411f
8 changed files with 206 additions and 17 deletions

View File

@ -180,18 +180,26 @@ IDBFactory::CreateForWindow(nsPIDOMWindow* aWindow,
// static
nsresult
IDBFactory::CreateForChromeJS(JSContext* aCx,
JS::Handle<JSObject*> aOwningObject,
IDBFactory** aFactory)
IDBFactory::CreateForMainThreadJS(JSContext* aCx,
JS::Handle<JSObject*> aOwningObject,
IDBFactory** aFactory)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(nsContentUtils::IsCallerChrome());
nsAutoPtr<PrincipalInfo> principalInfo(
new PrincipalInfo(SystemPrincipalInfo()));
nsAutoPtr<PrincipalInfo> principalInfo(new PrincipalInfo());
nsIPrincipal* principal = nsContentUtils::ObjectPrincipal(aOwningObject);
MOZ_ASSERT(principal);
bool isSystem;
if (!AllowedForPrincipal(principal, &isSystem)) {
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
nsresult rv =
CreateForMainThreadJSInternal(aCx, aOwningObject, principalInfo, aFactory);
nsresult rv = PrincipalToPrincipalInfo(principal, principalInfo);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = CreateForMainThreadJSInternal(aCx, aOwningObject, principalInfo, aFactory);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}

View File

@ -86,9 +86,9 @@ public:
IDBFactory** aFactory);
static nsresult
CreateForChromeJS(JSContext* aCx,
JS::Handle<JSObject*> aOwningObject,
IDBFactory** aFactory);
CreateForMainThreadJS(JSContext* aCx,
JS::Handle<JSObject*> aOwningObject,
IDBFactory** aFactory);
static nsresult
CreateForDatastore(JSContext* aCx,

View File

@ -582,7 +582,6 @@ IndexedDatabaseManager::DefineIndexedDB(JSContext* aCx,
JS::Handle<JSObject*> aGlobal)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(nsContentUtils::IsCallerChrome(), "Only for chrome!");
MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL,
"Passed object is not a global object!");
@ -609,9 +608,9 @@ IndexedDatabaseManager::DefineIndexedDB(JSContext* aCx,
}
nsRefPtr<IDBFactory> factory;
if (NS_FAILED(IDBFactory::CreateForChromeJS(aCx,
aGlobal,
getter_AddRefs(factory)))) {
if (NS_FAILED(IDBFactory::CreateForMainThreadJS(aCx,
aGlobal,
getter_AddRefs(factory)))) {
return false;
}
@ -940,7 +939,7 @@ IndexedDatabaseManager::LoggingModePrefChangedCallback(
return;
}
bool useProfiler =
bool useProfiler =
#if defined(DEBUG) || defined(MOZ_ENABLE_PROFILER_SPS)
Preferences::GetBool(kPrefLoggingProfiler);
#if !defined(MOZ_ENABLE_PROFILER_SPS)

View File

@ -332,6 +332,8 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || (buildapp == 'mulet') # Bug 931116 # TC: Bug 1144079 - Re-enable Mulet mochitests and reftests taskcluster-specific disables.
[test_request_readyState.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_sandbox.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_setVersion.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_setVersion_abort.html]

View File

@ -0,0 +1,101 @@
<!doctype html>
<html>
<head>
<title>indexedDB in JS Sandbox</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"></link>
</head>
<body>
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
// This runs inside a same-origin sandbox.
// The intent being to show that the data store is the same.
function storeValue() {
function createDB_inner() {
var op = indexedDB.open('db');
op.onupgradeneeded = e => {
var db = e.target.result;
db.createObjectStore('store');
};
return new Promise(resolve => {
op.onsuccess = e => resolve(e.target.result);
});
}
function add(k, v) {
return createDB_inner().then(db => {
var tx = db.transaction('store', 'readwrite');
var store = tx.objectStore('store');
var op = store.add(v, k);
return new Promise((resolve, reject) => {
op.onsuccess = e => resolve(e.target.result);
op.onerror = _ => reject(op.error);
tx.onabort = _ => reject(tx.error);
});
});
}
return add('x', [ 10, {} ])
.then(_ => step_done(),
_ => ok(false, 'failed to store'));
}
function createDB_outer() {
var op = indexedDB.open('db');
op.onupgradeneeded = e => {
ok(false, 'upgrade should not be needed');
var db = e.target.result;
db.createObjectStore('store');
};
return new Promise(resolve => {
op.onsuccess = e => resolve(e.target.result);
});
}
function get(k) {
return createDB_outer().then(db => {
var tx = db.transaction('store', 'readonly');
var store = tx.objectStore('store');
var op = store.get(k);
return new Promise((resolve, reject) => {
op.onsuccess = e => resolve(e.target.result);
op.onerror = _ => reject(op.error);
tx.onabort = _ => reject(tx.error);
});
});
}
function runInSandbox(sandbox, testFunc) {
is(typeof testFunc, 'function');
var resolvePromise;
var testPromise = new Promise(r => resolvePromise = r);
SpecialPowers.Cu.exportFunction(_ => resolvePromise(), sandbox,
{ defineAs: 'step_done' });
SpecialPowers.Cu.evalInSandbox('(' + testFunc.toSource() + ')()' +
'.then(step_done);', sandbox);
return testPromise;
}
// Use the window principal for the sandbox; location.origin is not sufficient.
var sb = new SpecialPowers.Cu.Sandbox(window,
{ wantGlobalProperties: ['indexedDB'] });
sb.ok = SpecialPowers.Cu.exportFunction(ok, sb);
Promise.resolve()
.then(_ => runInSandbox(sb, storeValue))
.then(_ => get('x'))
.then(x => {
ok(x, 'a value should be present');
is(x.length, 2);
is(x[0], 10);
is(typeof x[1], 'object');
is(Object.keys(x[1]).length, 0);
})
.then(_ => SimpleTest.finish());
</script>
</body>
</html>

View File

@ -0,0 +1,78 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
function exerciseInterface() {
function DB(name, store) {
this.name = name;
this.store = store;
this._db = this._create();
}
DB.prototype = {
_create: function() {
var op = indexedDB.open(this.name);
op.onupgradeneeded = e => {
var db = e.target.result;
db.createObjectStore(this.store);
};
return new Promise(resolve => {
op.onsuccess = e => resolve(e.target.result);
});
},
_result: function(tx, op) {
return new Promise((resolve, reject) => {
op.onsuccess = e => resolve(e.target.result);
op.onerror = () => reject(op.error);
tx.onabort = () => reject(tx.error);
});
},
get: function(k) {
return this._db.then(db => {
var tx = db.transaction(this.store, 'readonly');
var store = tx.objectStore(this.store);
return this._result(tx, store.get(k));
});
},
add: function(k, v) {
return this._db.then(db => {
var tx = db.transaction(this.store, 'readwrite');
var store = tx.objectStore(this.store);
return this._result(tx, store.add(v, k));
});
}
};
var db = new DB('data', 'base');
return db.add('x', [ 10, {} ])
.then(_ => db.get('x'))
.then(x => {
equal(x.length, 2);
equal(x[0], 10);
equal(typeof x[1], 'object');
equal(Object.keys(x[1]).length, 0);
});
}
function run_test() {
do_get_profile();
let Cu = Components.utils;
let sb = new Cu.Sandbox('https://www.example.com',
{ wantGlobalProperties: ['indexedDB'] });
sb.equal = equal;
var innerPromise = new Promise((resolve, reject) => {
sb.test_done = resolve;
sb.test_error = reject;
});
Cu.evalInSandbox('(' + exerciseInterface.toSource() + ')()' +
'.then(test_done, test_error);', sb);
Cu.importGlobalProperties(['indexedDB']);
do_test_pending();
Promise.all([innerPromise, exerciseInterface()])
.then(do_test_finished);
}

View File

@ -59,6 +59,7 @@ skip-if = toolkit == 'android' # bug 1079278
[test_remove_index.js]
[test_remove_objectStore.js]
[test_request_readyState.js]
[test_sandbox.js]
[test_setVersion.js]
[test_setVersion_abort.js]
[test_setVersion_events.js]

View File

@ -916,7 +916,7 @@ xpc::GlobalProperties::Define(JSContext* cx, JS::HandleObject obj)
if (CSS && !dom::CSSBinding::GetConstructorObject(cx, obj))
return false;
if (indexedDB && AccessCheck::isChrome(obj) &&
if (indexedDB &&
!IndexedDatabaseManager::DefineIndexedDB(cx, obj))
return false;