Bug 833633 - Remember permissions granted by users for PROMPT_ACTION through an update. r=ferjm, a=lmandel
authorAntonio M. Amaya <amac@tid.es>
Wed, 11 Jun 2014 04:47:00 -0400
changeset 207022 abd8f8a2645e616b9c320a6a6c9af3df5973bcba
parent 207021 769f119194b72d7d71e727d3e558787645f66ef2
child 207023 f10c7d6face5bb64886fab86bc1abe09569a5356
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersferjm, lmandel
bugs833633
milestone32.0a2
Bug 833633 - Remember permissions granted by users for PROMPT_ACTION through an update. r=ferjm, a=lmandel
dom/apps/src/PermissionsInstaller.jsm
dom/apps/src/Webapps.jsm
dom/apps/tests/file_packaged_app.template.webapp
dom/apps/tests/test_packaged_app_update.html
--- a/dom/apps/src/PermissionsInstaller.jsm
+++ b/dom/apps/src/PermissionsInstaller.jsm
@@ -51,17 +51,18 @@ this.PermissionsInstaller = {
    *        The just-installed app configuration.
    *        The properties used are manifestURL, origin and manifest.
    * @param boolean aIsReinstall
    *        Indicates the app was just re-installed
    * @param function aOnError
    *        A function called if an error occurs
    * @returns void
    **/
-  installPermissions: function installPermissions(aApp, aIsReinstall, aOnError) {
+  installPermissions: function installPermissions(aApp, aIsReinstall, aOnError,
+                                                  aIsSystemUpdate) {
     try {
       let newManifest = new ManifestHelper(aApp.manifest, aApp.origin);
       if (!newManifest.permissions && !aIsReinstall) {
         return;
       }
 
       if (aIsReinstall) {
         // Compare the original permissions against the new permissions
@@ -140,32 +141,47 @@ this.PermissionsInstaller = {
                " is not a valid Webapps permission name.");
           continue;
         }
 
         let expandedPermNames =
           expandPermissions(permName,
                             newManifest.permissions[permName].access);
         for (let idx in expandedPermNames) {
+
+          let isPromptPermission =
+            PermissionsTable[permName][appStatus] === PROMPT_ACTION;
+
           // We silently upgrade the permission to whatever the permission
           // is for certified apps (ALLOW or PROMPT) only if the
           // following holds true:
           // * The app is preinstalled
           // * The permission that would be granted is PROMPT
           // * The app is privileged
           let permission =
-            aApp.isPreinstalled &&
-            PermissionsTable[permName][appStatus] === PROMPT_ACTION &&
+            aApp.isPreinstalled && isPromptPermission &&
             appStatus === "privileged"
                 ? PermissionsTable[permName]["certified"]
                 : PermissionsTable[permName][appStatus];
 
-          this._setPermission(expandedPermNames[idx],
-                              PERM_TO_STRING[permission],
-                              aApp);
+          let permValue = PERM_TO_STRING[permission];
+          if (!aIsSystemUpdate && isPromptPermission) {
+            // If it's not a system update, then we should keep the prompt
+            // permissions that have been granted or denied previously.
+            permValue =
+              PermissionSettingsModule.getPermission(permName,
+                                                     aApp.manifestURL,
+                                                     aApp.origin,
+                                                     false);
+            if (permValue === "unknown") {
+              permValue = PERM_TO_STRING[permission];
+            }
+          }
+
+          this._setPermission(expandedPermNames[idx], permValue, aApp);
         }
       }
     }
     catch (ex) {
       dump("Caught webapps install permissions error for " + aApp.origin);
       Cu.reportError(ex);
       if (aOnError) {
         aOnError();
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -348,32 +348,33 @@ this.DOMApplicationRegistry = {
     // Create or Update the DataStore for this app
     this._readManifests([{ id: aId }]).then((aResult) => {
       let app = this.webapps[aId];
       this.updateDataStore(app.localId, app.origin, app.manifestURL,
                            aResult[0].manifest, app.appStatus);
     });
   },
 
-  updatePermissionsForApp: function(aId, aIsPreinstalled) {
+  updatePermissionsForApp: function(aId, aIsPreinstalled, aIsSystemUpdate) {
     if (!this.webapps[aId]) {
       return;
     }
 
     // Install the permissions for this app, as if we were updating
     // to cleanup the old ones if needed.
     // TODO It's not clear what this should do when there are multiple profiles.
     if (supportUseCurrentProfile()) {
       this._readManifests([{ id: aId }]).then((aResult) => {
         let data = aResult[0];
         PermissionsInstaller.installPermissions({
           manifest: data.manifest,
           manifestURL: this.webapps[aId].manifestURL,
           origin: this.webapps[aId].origin,
-          isPreinstalled: aIsPreinstalled
+          isPreinstalled: aIsPreinstalled,
+          isSystemUpdate: aIsSystemUpdate
         }, true, function() {
           debug("Error installing permissions for " + aId);
         });
       });
     }
   },
 
   updateOfflineCacheForApp: function(aId) {
@@ -595,17 +596,18 @@ this.DOMApplicationRegistry = {
         // At first run, install preloaded apps and set up their permissions.
         for (let id in this.webapps) {
           let isPreinstalled = this.installPreinstalledApp(id);
           this.removeIfHttpsDuplicate(id);
           if (!this.webapps[id]) {
             continue;
           }
           this.updateOfflineCacheForApp(id);
-          this.updatePermissionsForApp(id, isPreinstalled);
+          this.updatePermissionsForApp(id, isPreinstalled,
+                                       true /* isSystemUpdate */);
         }
         // Need to update the persisted list of apps since
         // installPreinstalledApp() removes the ones failing to install.
         this._saveApps();
       }
 
       // DataStores must be initialized at startup.
       for (let id in this.webapps) {
--- a/dom/apps/tests/file_packaged_app.template.webapp
+++ b/dom/apps/tests/file_packaged_app.template.webapp
@@ -1,13 +1,19 @@
 {
   "name" : "NAMETOKEN",
   "version" : "VERSIONTOKEN",
   "size" : PACKAGESIZETOKEN,
   "package_path": "PACKAGEPATHTOKEN",
   "description": "Updated even faster than Firefox, just to annoy slashdotters",
+  "permissions": {
+     "geolocation": {},
+     "audio-capture": {},
+     "video-capture": {},
+     "downloads": {}
+   },
   "launch_path": "tests/dom/apps/tests/file_packaged_app.sjs",
   "developer": {
     "name": "DEVELOPERTOKEN",
     "url": "DEVELOPERURLTOKEN"
   },
   "default_locale": "en-US"
 }
--- a/dom/apps/tests/test_packaged_app_update.html
+++ b/dom/apps/tests/test_packaged_app_update.html
@@ -50,17 +50,17 @@ function checkForUpdate(aExpected, aOnSu
     } else {
       PackagedTestHelper.next();
     }
   };
 }
 
 function checkLastAppState(aMiniManifestURL, aExpectedReady, aExpectedDownload,
                            aExpectedVersion, aCb) {
-  ok(true, aExpectedReady ? "App downloaded" : "App download applied");
+  info(aExpectedReady ? "App downloaded" : "App download applied");
   var expected = {
     name: PackagedTestHelper.gAppName,
     manifestURL: aMiniManifestURL,
     installOrigin: PackagedTestHelper.gInstallOrigin,
     progress: 0,
     installState: aExpectedReady ? "updating" : "installed",
     downloadAvailable: aExpectedDownload,
     downloading: false,
@@ -86,115 +86,192 @@ function updateApp(aExpectedReady, aPrev
         navigator.mozApps.mgmt.applyDownload(lApp);
     });
 
     checkForUpdate(true, ondownloadsuccesshandler, ondownloadappliedhandler, null,
                    true);
 
 }
 
+var initialPermissionState = {
+  "geolocation": "prompt",
+  "audio-capture": "prompt",
+  "video-capture": "prompt",
+  "downloads": "deny"
+}
+
+var permissionsToSet = {
+  "geolocation": "allow",
+  "audio-capture": "deny"
+}
+
+var permissionsToCheck = {
+  "geolocation": "allow",
+  "audio-capture": "deny",
+  "video-capture": "prompt",
+  "downloads": "deny"
+}
+
+function validatePermissions(aList, aDontFail) {
+  var gApp = PackagedTestHelper.gApp;
+  var mozPermissions = window.navigator.mozPermissionSettings;
+  var permission;
+  for (permission in aList) {
+    var permValue = mozPermissions.get(permission, gApp.manifestURL,
+                                       gApp.origin, false);
+    var wouldFail = permValue != aList[permission];
+    var checkFun = (aDontFail && wouldFail) ? todo_is : is;
+    checkFun(permValue, aList[permission],
+            "Permission " + permission + " should be " + aList[permission]);
+  }
+}
+
 
 var steps = [
   function() {
     // Set up
     SpecialPowers.setAllAppsLaunchable(true);
     SpecialPowers.addPermission("webapps-manage", true, document);
-    ok(true, "Set up");
-    PackagedTestHelper.next();
+    info("Set up");
+    // Note that without useCurrentProfile the permissions just aren't added.
+    SpecialPowers.pushPermissions(
+      [{'type': 'permissions', 'allow': true, 'context': document}],
+      function() {
+        SpecialPowers.pushPrefEnv(
+           {"set": [["dom.mozPermissionSettings.enabled", true],
+                    ["dom.webapps.useCurrentProfile", true]]},
+           PackagedTestHelper.next);
+      }
+    );
   },
   function() {
-    ok(true, "autoConfirmAppInstall");
+    info("autoConfirmAppInstall");
     SpecialPowers.autoConfirmAppInstall(PackagedTestHelper.next);
   },
   function() {
     PackagedTestHelper.setAppVersion(2, PackagedTestHelper.next);
   },
   function() {
-    ok(true, "== TEST == Install packaged app");
+    info("== TEST == Install packaged app");
     navigator.mozApps.mgmt.oninstall = function(evt) {
-      ok(true, "Got oninstall event");
+      info("Got oninstall event");
       PackagedTestHelper.gApp = evt.application;
       PackagedTestHelper.gApp.ondownloaderror = function() {
         ok(false, "Download error " + PackagedTestHelper.gApp.downloadError.name);
         PackagedTestHelper.finish();
       };
       PackagedTestHelper.gApp.ondownloadsuccess =
         checkLastAppState.bind(undefined, miniManifestURL, false, false,
                                2, PackagedTestHelper.next);
     };
 
     var request = navigator.mozApps.installPackage(miniManifestURL);
     request.onerror = PackagedTestHelper.mozAppsError;
     request.onsuccess = function() {
-      ok(true, "Application installed");
+      info("Application installed");
     };
   },
   function() {
-    ok(true, "== TEST == Check for Update and try to download it without update available");
+    info("== TEST == Permissions installed correctly");
+    validatePermissions(initialPermissionState);
+    PackagedTestHelper.next();
+  },
+  function() {
+    info("== TEST == Check for Update and try to download it without update available");
 
     function onerror() {
       is(PackagedTestHelper.gApp.downloadError.name, "NO_DOWNLOAD_AVAILABLE", "Download not available");
       ok(!PackagedTestHelper.gApp.readyToApplyDownload, "Not ready to apply download");
       PackagedTestHelper.next();
     }
     function onsuccess() {
       ok(false, "ondownloadsuccess fired");
       PackagedTestHelper.next();
     }
 
     checkForUpdate(false, onsuccess, null, onerror, true);
   },
   function() {
+    info("== TEST == Remember permissions");
+    var gApp = PackagedTestHelper.gApp;
+    var mozPermissions = window.navigator.mozPermissionSettings;
+    var permission;
+    for (permission in permissionsToSet) {
+      try {
+        window.navigator.mozPermissionSettings.set(permission,
+                                                   permissionsToSet[permission],
+                                                   gApp.manifestURL,
+                                                   gApp.origin, false);
+      } catch (e) {
+        ok(false,
+           "mozPermissionSettings.set failed for " + permission + " - " + e);
+      }
+    }
+    PackagedTestHelper.next();
+  },
+  function() {
+    info("== TEST == Check that the permissions have been saved");
+    // Since the permission API isn't really synchronous, just log any errors
+    // here.
+    validatePermissions(permissionsToCheck, true /*dontFail*/);
+    PackagedTestHelper.next();
+  },
+  function() {
     PackagedTestHelper.setAppVersion(3, PackagedTestHelper.next);
   },
   function() {
-    ok(true, "== TEST == Update packaged app");
+    info("== TEST == Update packaged app");
     updateApp(true, 2, 3);
   },
   function() {
-    ok(true, "== TEST == Check for Update after getting a new package");
+    info("== TEST == Check that saved permissions were kept");
+    validatePermissions(permissionsToCheck);
+    PackagedTestHelper.next();
+  },
+  function() {
+    info("== TEST == Check for Update after getting a new package");
     checkForUpdate(false);
   },
   function() {
     PackagedTestHelper.setAppVersion(4, PackagedTestHelper.next, true);
   },
   function() {
-    ok(true, "== TEST == Update packaged app - same package");
+    info("== TEST == Update packaged app - same package");
     updateApp(false, 3, 3);
   },
   function() {
-    ok(true, "== TEST == Check for Update after getting the same package");
+    info("== TEST == Check for Update after getting the same package");
     checkForUpdate(false);
   },
   function() {
     PackagedTestHelper.setAppVersion(1, PackagedTestHelper.next);
   },
   function() {
-    ok(true, "== TEST == Update packaged app - Updating a pending app");
+    info("== TEST == Update packaged app - Updating a pending app");
     miniManifestURL = PackagedTestHelper.gSJS +
                       "?getManifest=true" +
                       "&appName=arandomname" +
                       "&appToFail1";
     PackagedTestHelper.checkAppDownloadError(miniManifestURL,
                                             "MANIFEST_MISMATCH", 2, false, true,
                                              "arandomname",
                                              function () {
       checkForUpdate(false, null, null, null, false,
                      function (request) {
         if (request.error.name === "PENDING_APP_NOT_UPDATABLE") {
-          ok(true, "Got expected PENDING_APP_NOT_UPDATEABLE");
+          info("Got expected PENDING_APP_NOT_UPDATEABLE");
         } else {
           ok(false, "Got unexpected " + request.error.name);
         }
         PackagedTestHelper.next();
       });
     });
   },
   function() {
-    ok(true, "all done!\n");
+    info("all done!\n");
     PackagedTestHelper.finish();
   }
 ];
 
 PackagedTestHelper.setSteps(steps);
 // appToUpdate added to the URL so we get a unique URL for this app.
 // Unique in this case meaning different from the ones used on the
 // install tests