Bug 1111961 - Developer mode support r=ferjm,pauljt
authorFabrice Desré <fabrice@mozilla.com>
Mon, 13 Apr 2015 09:49:50 -0700
changeset 257704 56aa2a5662eb38227fa0ef06084cb18cbce353cb
parent 257703 3e70aa94af3e702431aeffd547f1752472042476
child 257705 92aa3a1717af6834349a5be34adb493ec42f8b13
push id8007
push userraliiev@mozilla.com
push dateMon, 11 May 2015 19:23:16 +0000
treeherdermozilla-aurora@e2ce1aac996e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersferjm, pauljt
bugs1111961
milestone40.0a1
Bug 1111961 - Developer mode support r=ferjm,pauljt
dom/apps/AppsUtils.jsm
dom/apps/ImportExport.jsm
dom/apps/PermissionsInstaller.jsm
dom/apps/Webapps.jsm
dom/apps/tests/file_hosted_certified.webapp
dom/apps/tests/file_hosted_certified.webapp^headers^
dom/apps/tests/mochitest.ini
dom/apps/tests/test_install_dev_mode.html
--- a/dom/apps/AppsUtils.jsm
+++ b/dom/apps/AppsUtils.jsm
@@ -502,16 +502,23 @@ this.AppsUtils = {
   /**
    * Checks if the app role is allowed:
    * Only certified apps can be themes.
    * Only privileged or certified apps can be addons.
    * @param aRole   : the role assigned to this app.
    * @param aStatus : the APP_STATUS_* for this app.
    */
   checkAppRole: function(aRole, aStatus) {
+    try {
+      // Anything is possible in developer mode.
+      if (Services.prefs.getBoolPref("dom.apps.developer_mode")) {
+        return true;
+      }
+    } catch(e) {}
+
     if (aRole == "theme" && aStatus !== Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
       return false;
     }
     if (!this.allowUnsignedAddons &&
         (aRole == "addon" &&
          aStatus !== Ci.nsIPrincipal.APP_STATUS_CERTIFIED &&
          aStatus !== Ci.nsIPrincipal.APP_STATUS_PRIVILEGED)) {
       return false;
--- a/dom/apps/ImportExport.jsm
+++ b/dom/apps/ImportExport.jsm
@@ -97,17 +97,22 @@ this.ImportExport = {
     debug("Exporting " + aApp.manifestURL);
 
     if (aApp.installState != "installed") {
       throw "AppNotFullyInstalled";
     }
 
     // Exporting certified apps is forbidden, as it is to import them.
     // We *have* to do this check in the parent process.
-    if (aApp.appStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
+    let devMode = false;
+    try {
+      devMode = Services.prefs.getBoolPref("dom.apps.developer_mode");
+    } catch(e) {};
+
+    if (aApp.appStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED && !devMode) {
       throw "CertifiedAppExportForbidden";
     }
 
     // Add the metadata we'll need to recreate the app object.
     let meta = {
       installOrigin: aApp.InstallOrigin,
       manifestURL: aApp.manifestURL,
       version: kAppArchiveVersion
@@ -386,16 +391,22 @@ this.ImportExport = {
       if (isPackage) {
         meta.origin = "app://" + meta.id;
         // Signature check
         // TODO: stop accessing internal methods of other objects.
         let [reader, isSigned] =
           yield DOMApplicationRegistry._openPackage(appFile, meta, false);
         let maxStatus = isSigned ? Ci.nsIPrincipal.APP_STATUS_PRIVILEGED
                                  : Ci.nsIPrincipal.APP_STATUS_INSTALLED;
+        try {
+          // Anything is possible in developer mode.
+          if (Services.prefs.getBoolPref("dom.apps.developer_mode")) {
+            maxStatus = Ci.nsIPrincipal.APP_STATUS_CERTIFIED;
+          }
+        } catch(e) {};
         meta.appStatus = AppsUtils.getAppManifestStatus(manifest);
         debug("Signed app? " + isSigned);
         if (meta.appStatus > maxStatus) {
           throw "InvalidPrivilegeLevel";
         }
 
         // Custom origin.
         // We unfortunately can't reuse _checkOrigin here.
--- a/dom/apps/PermissionsInstaller.jsm
+++ b/dom/apps/PermissionsInstaller.jsm
@@ -173,17 +173,18 @@ this.PermissionsInstaller = {
             }
           }
 
           this._setPermission(expandedPermNames[idx], permValue, aApp);
         }
       }
     }
     catch (ex) {
-      dump("Caught webapps install permissions error for " + aApp.origin);
+      dump("Caught webapps install permissions error for " + aApp.origin +
+        " : " + ex + "\n");
       Cu.reportError(ex);
       if (aOnError) {
         aOnError();
       }
     }
   },
 
   /**
--- a/dom/apps/Webapps.jsm
+++ b/dom/apps/Webapps.jsm
@@ -2554,16 +2554,23 @@ this.DOMApplicationRegistry = {
           return;
         }
       }
     }
 
     // Hosted apps can't be trusted or certified, so just check that the
     // manifest doesn't ask for those.
     function checkAppStatus(aManifest) {
+      try {
+        // Everything is authorized in developer mode.
+        if (Services.prefs.getBoolPref("dom.apps.developer_mode")) {
+          return true;
+        }
+      } catch(e) {}
+
       let manifestStatus = aManifest.type || "web";
       return manifestStatus === "web" ||
              manifestStatus === "trusted";
     }
 
     let checkManifest = (function() {
       if (!app.manifest) {
         sendError("MANIFEST_PARSE_ERROR");
@@ -3899,20 +3906,28 @@ this.DOMApplicationRegistry = {
       throw "INSTALL_FROM_DENIED";
     }
 
     // Local file installs can be privileged even without the signature.
     let maxStatus = aIsSigned || aIsLocalFileInstall
                     ? Ci.nsIPrincipal.APP_STATUS_PRIVILEGED
                     : Ci.nsIPrincipal.APP_STATUS_INSTALLED;
 
+    try {
+      // Anything is possible in developer mode.
+      if (Services.prefs.getBoolPref("dom.apps.developer_mode")) {
+        maxStatus = Ci.nsIPrincipal.APP_STATUS_CERTIFIED;
+      }
+    } catch(e) {};
+
     let allowUnsignedLangpack = false;
     try  {
       allowUnsignedLangpack =
-        Services.prefs.getBoolPref("dom.apps.allow_unsigned_langpacks");
+        Services.prefs.getBoolPref("dom.apps.allow_unsigned_langpacks") ||
+        Services.prefs.getBoolPref("dom.apps.developer_mode");
     } catch(e) {}
     let isLangPack = newManifest.role === "langpack" &&
                      (aIsSigned || allowUnsignedLangpack);
 
     let status = AppsUtils.getAppManifestStatus(newManifest);
     if (status > maxStatus && !isLangPack) {
       throw "INVALID_SECURITY_LEVEL";
     }
new file mode 100644
--- /dev/null
+++ b/dom/apps/tests/file_hosted_certified.webapp
@@ -0,0 +1,6 @@
+{
+  "name": "Certified hosted app",
+  "description": "An app that can't only be installed in dev mode.",
+  "launch_path": "/tests/dom/apps/tests/file_app.sjs?apptype=hosted",
+  "type": "certified"
+}
new file mode 100644
--- /dev/null
+++ b/dom/apps/tests/file_hosted_certified.webapp^headers^
@@ -0,0 +1,1 @@
+Content-Type: application/manifest+json
\ No newline at end of file
--- a/dom/apps/tests/mochitest.ini
+++ b/dom/apps/tests/mochitest.ini
@@ -7,16 +7,18 @@ support-files =
   addons/index.html
   chromeAddCert.js
   file_app.sjs
   file_app.template.html
   file_script.template.js
   file_cached_app.template.appcache
   file_cached_app.template.webapp
   file_hosted_app.template.webapp
+  file_hosted_certified.webapp
+  file_hosted_certified.webapp^headers^
   file_manifest.json
   file_manifest.json^headers^
   file_trusted_app.template.webapp
   file_invalidWidget_app.template.webapp
   file_packaged_app.sjs
   file_packaged_app.template.html
   file_packaged_app.template.webapp
   file_widget_app.template.webapp
@@ -35,16 +37,17 @@ support-files =
 
 [test_app_addons.html]
 skip-if = os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app
 [test_app_enabled.html]
 [test_app_update.html]
 skip-if = os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app
 [test_bug_795164.html]
 [test_import_export.html]
+[test_install_dev_mode.html]
 [test_install_multiple_apps_origin.html]
 [test_install_receipts.html]
 [test_langpacks.html]
 skip-if = os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app
 [test_marketplace_pkg_install.html]
 skip-if = buildapp == "b2g" || toolkit == "android" # see bug 989806
 [test_packaged_app_install.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
new file mode 100644
--- /dev/null
+++ b/dom/apps/tests/test_install_dev_mode.html
@@ -0,0 +1,122 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id={1111961}
+-->
+<head>
+  <title>Test for Bug {1111961}</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/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={1111961}">Mozilla Bug {1111961}</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript;version=1.7">
+
+var gManifestURL = "http://test/tests/dom/apps/tests/file_hosted_certified.webapp";
+var gGenerator = runTest();
+
+function go() {
+  SpecialPowers.pushPermissions(
+    [{ "type": "webapps-manage", "allow": 1, "context": document }],
+    function() { gGenerator.next() });
+}
+
+function continueTest() {
+  try {
+    gGenerator.next();
+  } catch (e if e instanceof StopIteration) {
+    finish();
+  }
+}
+
+function finish() {
+  SimpleTest.finish();
+}
+
+function cbError(aEvent) {
+  ok(false, "Error callback invoked " +
+            aEvent.target.error.name + " " + aEvent.target.error.message);
+  finish();
+}
+
+function cbSuccess(aMsg) {
+  return function(aEvent) {
+    ok(true, aMsg);
+    continueTest();
+  }
+}
+
+SimpleTest.waitForExplicitFinish();
+
+/**
+  * Install 2 apps from the same origin and uninstall them.
+  */
+function runTest() {
+  SpecialPowers.setAllAppsLaunchable(true);
+
+  SpecialPowers.autoConfirmAppInstall(continueTest);
+  yield undefined;
+
+  SpecialPowers.autoConfirmAppUninstall(continueTest);
+  yield undefined;
+
+  request = navigator.mozApps.mgmt.getAll();
+  request.onerror = cbError;
+  request.onsuccess = continueTest;
+  yield undefined;
+  var initialAppsCount = request.result.length;
+  info("Starting with " + initialAppsCount + " apps installed.");
+
+  // We are not in dev mode, so this install will fail.
+  var request = navigator.mozApps.install(gManifestURL, { });
+  request.onerror = cbSuccess("Can't install certified app without dev mode");
+  request.onsuccess = cbError;
+  yield undefined;
+
+  // Turn on dev mode.
+  SpecialPowers.pushPrefEnv({"set": [["dom.apps.developer_mode", true]]},
+                            continueTest);
+  yield undefined;
+
+  // Installation should succeed now.
+  request = navigator.mozApps.install(gManifestURL, { });
+  request.onerror = cbError;
+  request.onsuccess = cbSuccess("Install certified app in dev mode");;
+  yield undefined;
+
+  // Uninstall and check we cleaned up.
+  var app = request.result;
+
+  navigator.mozApps.mgmt.onuninstall = function(event) {
+    var app = event.application;
+    is(app.manifestURL, gManifestURL, "App uninstall event ok.");
+    continueTest();
+  }
+  request = navigator.mozApps.mgmt.uninstall(app);
+  request.onerror = cbError;
+  request.onsuccess = continueTest;
+  yield undefined;
+  yield undefined;
+  is(request.result, gManifestURL, "App uninstalled.");
+  navigator.mozApps.mgmt.onuninstall = null;
+
+  request = navigator.mozApps.mgmt.getAll();
+  request.onerror = cbError;
+  request.onsuccess = continueTest;
+  yield undefined;
+  is(request.result.length, initialAppsCount, "All apps are uninstalled.");
+}
+
+addLoadEvent(go);
+
+</script>
+</pre>
+</body>
+</html>