Bug 756645 - Implement IndexedDB apps isolation. r=bent
authorMounir Lamouri <mounir.lamouri@gmail.com>
Tue, 28 Aug 2012 09:46:46 -0300
changeset 105697 d192fe2404618a609ecb5274a2d432feff9e0d06
parent 105696 f50483a61327c694869231a8c43e8bea2c16c1c1
child 105698 dfc4199ccae4f8eb96464697225edfa152a22f7d
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
reviewersbent
bugs756645
milestone18.0a1
Bug 756645 - Implement IndexedDB apps isolation. r=bent
dom/indexedDB/IDBFactory.cpp
dom/indexedDB/IndexedDatabaseManager.cpp
dom/indexedDB/test/Makefile.in
dom/indexedDB/test/file_app_isolation.html
dom/indexedDB/test/file_app_isolation.js
dom/indexedDB/test/test_app_isolation_inproc.html
dom/indexedDB/test/test_app_isolation_oop.html
testing/mochitest/android.json
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -203,21 +203,16 @@ IDBFactory::Create(ContentParent* aConte
       NS_ASSERTION(!lastCx, "We should only be called from C++!");
     }
     else {
       NS_ERROR("nsIThreadJSContextStack::Peek should never fail!");
     }
   }
 #endif
 
-  nsCString origin;
-  nsresult rv =
-    IndexedDatabaseManager::GetASCIIOriginFromWindow(nullptr, origin);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   nsCOMPtr<nsIPrincipal> principal =
     do_CreateInstance("@mozilla.org/nullprincipal;1");
   NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
 
   JSContext* cx = nsContentUtils::GetSafeJSContext();
   NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
 
   nsCxPusher pusher;
@@ -227,17 +222,17 @@ IDBFactory::Create(ContentParent* aConte
   }
 
   JSAutoRequest ar(cx);
 
   nsIXPConnect* xpc = nsContentUtils::XPConnect();
   NS_ASSERTION(xpc, "This should never be null!");
 
   nsCOMPtr<nsIXPConnectJSObjectHolder> globalHolder;
-  rv = xpc->CreateSandbox(cx, principal, getter_AddRefs(globalHolder));
+  nsresult rv = xpc->CreateSandbox(cx, principal, getter_AddRefs(globalHolder));
   NS_ENSURE_SUCCESS(rv, rv);
 
   JSObject* global;
   rv = globalHolder->GetJSObject(&global);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // The CreateSandbox call returns a proxy to the actual sandbox object. We
   // don't need a proxy here.
@@ -626,9 +621,9 @@ IDBFactory::Cmp(const jsval& aFirst,
   }
 
   if (first.IsUnset() || second.IsUnset()) {
     return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   }
 
   *_retval = Key::CompareKeys(first, second);
   return NS_OK;
-}
\ No newline at end of file
+}
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -981,17 +981,17 @@ IndexedDatabaseManager::GetASCIIOriginFr
 
   nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
   NS_ENSURE_TRUE(principal, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (nsContentUtils::IsSystemPrincipal(principal)) {
     aASCIIOrigin.AssignLiteral("chrome");
   }
   else {
-    nsresult rv = nsContentUtils::GetASCIIOrigin(principal, aASCIIOrigin);
+    nsresult rv = principal->GetExtendedOrigin(aASCIIOrigin);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     if (aASCIIOrigin.EqualsLiteral("null")) {
       NS_WARNING("IndexedDB databases not allowed for this principal!");
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
   }
 
--- a/dom/indexedDB/test/Makefile.in
+++ b/dom/indexedDB/test/Makefile.in
@@ -96,16 +96,20 @@ MOCHITEST_FILES = \
   test_transaction_ordering.html \
   test_setVersion.html \
   test_setVersion_abort.html \
   test_setVersion_events.html \
   test_setVersion_exclusion.html \
   test_unique_index_update.html \
   third_party_iframe1.html \
   third_party_iframe2.html \
+  test_app_isolation_inproc.html \
+  test_app_isolation_oop.html \
+  file_app_isolation.html \
+  file_app_isolation.js \
   $(NULL)
 
 #   test_writer_starvation.html  disabled for infinite loops, bug 595368
 
 ifeq (browser,$(MOZ_BUILD_APP))
 MOCHITEST_BROWSER_FILES = \
   browser_forgetThisSite.js \
   browser_forgetThisSiteAdd.html \
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/file_app_isolation.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    foobar!
+  </body>
+  <script>
+    var data = [
+      { id: "0", name: "foo" },
+    ];
+
+    var action = window.location.search.substring(1);
+    var finished = false;
+    var created = false; // We use that for 'read-no' action.
+
+    function finish(value) {
+      value ? alert('success') : alert('failure');
+      finished = true;
+    }
+
+    var request = window.indexedDB.open('AppIsolationTest');
+
+    request.onupgradeneeded = function(event) {
+      if (finished) {
+        finish(false);
+        return;
+      }
+
+      switch (action) {
+        case 'read-no':
+          created = true;
+          break;
+        case 'read-yes':
+          finish(false);
+          break;
+        case 'write':
+          created = true;
+
+          var db = event.target.result;
+
+          var objectStore = db.createObjectStore("test", { keyPath: "id" });
+          for (var i in data) {
+            objectStore.add(data[i]);
+          }
+          break;
+      }
+    }
+
+    request.onsuccess = function(event) {
+      if (finished) {
+        finish(false);
+        return;
+      }
+
+      var db = event.target.result;
+
+      // Think about close the db!
+      switch (action) {
+        case 'read-no':
+          db.close();
+
+          if (created) { // That means we have created it.
+            indexedDB.deleteDatabase('AppIsolationTest').onsuccess = function() {
+              finish(true);
+            };
+          } else {
+            finish(false);
+          }
+          break;
+        case 'read-yes':
+          db.transaction("test").objectStore("test").get("0").onsuccess = function(event) {
+            var name = event.target.result.name;
+            db.close();
+
+            indexedDB.deleteDatabase('AppIsolationTest').onsuccess = function() {
+              finish(name == 'foo');
+            };
+          };
+          break;
+        case 'write':
+          db.close();
+
+          // Success only if the db was actually created.
+          finish(created);
+          break;
+      }
+    };
+  </script>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/file_app_isolation.js
@@ -0,0 +1,169 @@
+SimpleTest.waitForExplicitFinish();
+
+var fileTestOnCurrentOrigin = (location.protocol + '//' + location.host + location.pathname)
+                              .replace('test_', 'file_')
+                              .replace('_inproc', '').replace('_oop', '');
+
+var previousPrefs = {
+  mozBrowserFramesEnabled: undefined,
+  mozBrowserFramesWhitelist: undefined,
+  oop_by_default: undefined,
+};
+
+try {
+  previousPrefs.mozBrowserFramesEnabled = SpecialPowers.getBoolPref('dom.mozBrowserFramesEnabled');
+} catch(e)
+{
+}
+
+try {
+  previousPrefs.mozBrowserFramesWhitelist = SpecialPowers.getCharPref('dom.mozBrowserFramesWhitelist');
+} catch(e) {
+}
+
+try {
+  previousPrefs.oop_by_default = SpecialPowers.getBoolPref('dom.ipc.browser_frames.oop_by_default');
+} catch(e) {
+}
+
+SpecialPowers.setBoolPref('dom.mozBrowserFramesEnabled', true);
+SpecialPowers.setCharPref('dom.mozBrowserFramesWhitelist', location.protocol + "//" + location.host);
+SpecialPowers.setBoolPref("dom.ipc.browser_frames.oop_by_default", location.pathname.indexOf('_inproc') == -1);
+
+var gData = [
+  // APP 1
+  {
+    app: 'http://example.org/manifest.webapp',
+    action: 'read-no',
+    src: fileTestOnCurrentOrigin,
+  },
+  {
+    app: 'http://example.org/manifest.webapp',
+    action: 'write',
+    src: fileTestOnCurrentOrigin,
+  },
+  {
+    app: 'http://example.org/manifest.webapp',
+    action: 'read-yes',
+    src: fileTestOnCurrentOrigin,
+  },
+  // APP 2
+  {
+    app: 'https://example.com/manifest.webapp',
+    action: 'read-no',
+    src: fileTestOnCurrentOrigin,
+  },
+  {
+    app: 'https://example.com/manifest.webapp',
+    action: 'write',
+    src: fileTestOnCurrentOrigin,
+  },
+  {
+    app: 'https://example.com/manifest.webapp',
+    action: 'read-yes',
+    src: fileTestOnCurrentOrigin,
+  },
+  // Browser
+  {
+    browser: true,
+    action: 'read-no',
+    src: fileTestOnCurrentOrigin,
+  },
+  {
+    browser: true,
+    action: 'write',
+    src: fileTestOnCurrentOrigin,
+  },
+  {
+    browser: true,
+    action: 'read-yes',
+    src: fileTestOnCurrentOrigin,
+  },
+];
+
+function runTest() {
+  for (var i in gData) {
+    var iframe = document.createElement('iframe');
+    var data = gData[i];
+
+    if (data.app) {
+      iframe.setAttribute('mozbrowser', '');
+      iframe.setAttribute('mozapp', data.app);
+    } else if (data.browser) {
+      iframe.setAttribute('mozbrowser', '');
+    }
+
+    if (data.app || data.browser) {
+      iframe.addEventListener('mozbrowsershowmodalprompt', function(e) {
+        is(e.detail.message, 'success', 'test number ' + i);
+
+//        document.getElementById('content').removeChild(iframe);
+
+        i++;
+        if (i >= gData.length) {
+          if (previousPrefs.mozBrowserFramesEnabled !== undefined) {
+            SpecialPowers.setBoolPref('dom.mozBrowserFramesEnabled', previousPrefs.mozBrowserFramesEnabled);
+          }
+          if (previousPrefs.mozBrowserFramesWhitelist !== undefined) {
+            SpecialPowers.setCharPref('dom.mozBrowserFramesWhitelist', previousPrefs.mozBrowserFramesWhitelist);
+          }
+          if (previousPrefs.oop_by_default !== undefined) {
+            SpecialPowers.setBoolPref("dom.ipc.browser_frames.oop_by_default", previousPrefs.oop_by_default);
+          }
+
+          indexedDB.deleteDatabase('AppIsolationTest').onsuccess = function() {
+            SimpleTest.finish();
+          };
+        } else {
+          gTestRunner.next();
+        }
+      });
+    }
+
+    iframe.src = data.src + '?' + data.action;
+
+    document.getElementById('content').appendChild(iframe);
+
+    yield;
+  }
+}
+
+var gTestRunner = runTest();
+
+function startTest() {
+  var request = window.indexedDB.open('AppIsolationTest');
+  var created = false;
+
+  request.onupgradeneeded = function(event) {
+    created = true;
+    var db = event.target.result;
+    var data = [
+      { id: "0", name: "foo" },
+    ];
+    var objectStore = db.createObjectStore("test", { keyPath: "id" });
+    for (var i in data) {
+      objectStore.add(data[i]);
+    }
+  }
+
+  request.onsuccess = function(event) {
+    var db = event.target.result;
+    is(created, true, "we should have created the db");
+
+    db.transaction("test").objectStore("test").get("0").onsuccess = function(event) {
+      is(event.target.result.name, 'foo', 'data have been written');
+      db.close();
+
+      gTestRunner.next();
+    };
+  }
+}
+
+// test_ipc.html executes all the tests in this directory in content process.
+// It will fail on this one for the moment.
+if (!SpecialPowers.isMainProcess()) {
+  todo(false, "We should make this work on content process");
+  SimpleTest.finish();
+} else {
+  startTest();
+}
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/test_app_isolation_inproc.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=756645
+-->
+<head>
+  <title>Test for IndexedDB app isolation (unique process)</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=756645">Mozilla Bug 756645</a>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.7" src="file_app_isolation.js">
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/test_app_isolation_oop.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=756645
+-->
+<head>
+  <title>Test for IndexedDB app isolation (unique process)</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=756645">Mozilla Bug 756645</a>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.7" src="file_app_isolation.js">
+</script>
+</pre>
+</body>
+</html>
--- a/testing/mochitest/android.json
+++ b/testing/mochitest/android.json
@@ -142,16 +142,18 @@
  "dom/imptests/webapps/DOMCore/tests/approved/test_Range-insertNode.html": "bug 775227",
  "dom/imptests/webapps/DOMCore/tests/approved/test_Range-mutations.html": "bug 775227",
  "dom/imptests/webapps/DOMCore/tests/approved/test_Range-set.html": "bug 775227",
  "dom/imptests/webapps/DOMCore/tests/approved/test_Range-surroundContents.html": "bug 775227",
  "dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_key.html": "bug 775227",
  "dom/indexedDB/ipc/test_ipc.html": "bug 783513",
  "dom/indexedDB/test/test_third_party.html": "TIMED_OUT",
  "dom/indexedDB/test/test_event_propagation.html": "TIMED_OUT, bug 780855",
+ "dom/indexedDB/test/test_app_isolation_inproc.html": "TIMED_OUT",
+ "dom/indexedDB/test/test_app_isolation_oop.html": "TIMED_OUT",
  "dom/network/tests/test_network_basics.html": "",
  "dom/settings/tests/test_settings_events.html": "",
  "dom/settings/tests/test_settings_basics.html": "",
  "dom/contacts/tests/test_contacts_blobs.html": "",
  "dom/contacts/tests/test_contacts_basics.html": "",
  "dom/contacts/tests/test_contacts_events.html": "",
  "dom/sms/tests/test_sms_basics.html": "",
  "dom/tests/mochitest/ajax/jquery/test_jQuery.html": "bug 775227",