Bug 1081873 - [Loop][Contacts API] mozContacts API should trigger DOMRequest.onerror if no permissions are granted to the consumer. r=anygregor
☠☠ backed out by 081bbd8a6676 ☠ ☠
authorFernando Jiménez <ferjmoreno@gmail.com>
Thu, 16 Oct 2014 11:25:35 +0200
changeset 210707 f461eb1653f1ef28b29029e7e0d00363cd2f2152
parent 210577 9fe9bace9ed54b25663ae2c0d3a10a83ee3eb2de
child 210708 528b832ab47813351a1d689561235588c3cd45ed
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersanygregor
bugs1081873
milestone36.0a1
Bug 1081873 - [Loop][Contacts API] mozContacts API should trigger DOMRequest.onerror if no permissions are granted to the consumer. r=anygregor
dom/contacts/ContactManager.js
dom/contacts/tests/mochitest.ini
dom/contacts/tests/test_permission_denied.html
--- a/dom/contacts/ContactManager.js
+++ b/dom/contacts/ContactManager.js
@@ -251,17 +251,17 @@ ContactManager.prototype = {
     if (permValue == Ci.nsIPermissionManager.ALLOW_ACTION) {
       if (aAllowCallback) {
         aAllowCallback();
       }
       return;
     } else if (permValue == Ci.nsIPermissionManager.DENY_ACTION ||
                permValue == Ci.nsIPermissionManager.UNKNOWN_ACTION) {
       if (aCancelCallback) {
-        aCancelCallback();
+        aCancelCallback("PERMISSION_DENIED");
       }
       return;
     }
 
     // Create an array with a single nsIContentPermissionType element.
     type = {
       type: "contacts",
       access: access,
@@ -271,26 +271,24 @@ ContactManager.prototype = {
     let typeArray = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
     typeArray.appendElement(type, false);
 
     // create a nsIContentPermissionRequest
     let request = {
       types: typeArray,
       principal: principal,
       QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionRequest]),
-      allow: aAllowCallback ||
-             function() {
-               if (DEBUG)
-                 debug("Default allow contacts callback. " + access +"\n");
-             },
-      cancel: aCancelCallback ||
-              function() {
-                if (DEBUG)
-                  debug("Default cancel contacts callback. " + access +"\n");
-              },
+      allow: function() {
+        aAllowCallback && aAllowCallback();
+        DEBUG && debug("Permission granted. Access " + access +"\n");
+      },
+      cancel: function() {
+        aCancelCallback && aCancelCallback("PERMISSION_DENIED");
+        DEBUG && debug("Permission denied. Access " + access +"\n");
+      },
       window: this._window
     };
 
     // Using askPermission from nsIDOMWindowUtils that takes care of the
     // remoting if needed.
     let windowUtils = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIDOMWindowUtils);
     windowUtils.askPermission(request);
@@ -331,30 +329,47 @@ ContactManager.prototype = {
     newContact.id = aContact.id;
     newContact.published = aContact.published;
     newContact.updated = aContact.updated;
 
     if (DEBUG) debug("send: " + JSON.stringify(newContact));
 
     let options = { contact: newContact, reason: reason };
     let allowCallback = function() {
-      cpmm.sendAsyncMessage("Contact:Save", {requestID: requestID, options: options});
-    }.bind(this)
-    this.askPermission(reason, request, allowCallback);
+      cpmm.sendAsyncMessage("Contact:Save", {
+        requestID: requestID,
+        options: options
+      });
+    }.bind(this);
+
+    let cancelCallback = function(reason) {
+      Services.DOMRequest.fireErrorAsync(request, reason);
+    };
+
+    this.askPermission(reason, request, allowCallback, cancelCallback);
     return request;
   },
 
   find: function(aOptions) {
     if (DEBUG) debug("find! " + JSON.stringify(aOptions));
     let request = this.createRequest();
     let options = { findOptions: aOptions };
+
     let allowCallback = function() {
-      cpmm.sendAsyncMessage("Contacts:Find", {requestID: this.getRequestId({request: request, reason: "find"}), options: options});
-    }.bind(this)
-    this.askPermission("find", request, allowCallback);
+      cpmm.sendAsyncMessage("Contacts:Find", {
+        requestID: this.getRequestId({request: request, reason: "find"}),
+        options: options
+      });
+    }.bind(this);
+
+    let cancelCallback = function(reason) {
+      Services.DOMRequest.fireErrorAsync(request, reason);
+    };
+
+    this.askPermission("find", request, allowCallback, cancelCallback);
     return request;
   },
 
   createCursor: function CM_createCursor(aRequest) {
     let data = {
       cursor: Services.DOMRequest.createCursor(this._window, function() {
         this.handleContinue(id);
       }.bind(this)),
@@ -364,21 +379,29 @@ ContactManager.prototype = {
     let id = this.getRequestId(data);
     if (DEBUG) debug("saved cursor id: " + id);
     return [id, data.cursor];
   },
 
   getAll: function CM_getAll(aOptions) {
     if (DEBUG) debug("getAll: " + JSON.stringify(aOptions));
     let [cursorId, cursor] = this.createCursor();
+
     let allowCallback = function() {
       cpmm.sendAsyncMessage("Contacts:GetAll", {
-        cursorId: cursorId, findOptions: aOptions});
+        cursorId: cursorId,
+        findOptions: aOptions
+      });
     }.bind(this);
-    this.askPermission("find", cursor, allowCallback);
+
+    let cancelCallback = function(reason) {
+      Services.DOMRequest.fireErrorAsync(cursor, reason);
+    };
+
+    this.askPermission("find", cursor, allowCallback, cancelCallback);
     return cursor;
   },
 
   nextTick: function nextTick(aCallback) {
     Services.tm.currentThread.dispatch(aCallback, Ci.nsIThread.DISPATCH_NORMAL);
   },
 
   handleContinue: function CM_handleContinue(aCursorId) {
@@ -407,62 +430,80 @@ ContactManager.prototype = {
     } else if (!aRecordOrId || !aRecordOrId.id) {
       Services.DOMRequest.fireErrorAsync(request, true);
       return request;
     } else {
       id = aRecordOrId.id;
     }
 
     let options = { id: id };
+
     let allowCallback = function() {
-      cpmm.sendAsyncMessage("Contact:Remove", {requestID: this.getRequestId({request: request, reason: "remove"}), options: options});
+      cpmm.sendAsyncMessage("Contact:Remove", {
+        requestID: this.getRequestId({request: request, reason: "remove"}),
+        options: options
+      });
     }.bind(this);
-    this.askPermission("remove", request, allowCallback);
+
+    let cancelCallback = function(reason) {
+      Services.DOMRequest.fireErrorAsync(request, reason);
+    };
+
+    this.askPermission("remove", request, allowCallback, cancelCallback);
     return request;
   },
 
   clear: function() {
     if (DEBUG) debug("clear");
     let request = this.createRequest();
     let options = {};
+
     let allowCallback = function() {
-      cpmm.sendAsyncMessage("Contacts:Clear", {requestID: this.getRequestId({request: request, reason: "remove"}), options: options});
+      cpmm.sendAsyncMessage("Contacts:Clear", {
+        requestID: this.getRequestId({request: request, reason: "remove"}),
+        options: options
+      });
     }.bind(this);
-    this.askPermission("remove", request, allowCallback);
+
+    let cancelCallback = function(reason) {
+      Services.DOMRequest.fireErrorAsync(request, reason);
+    };
+
+    this.askPermission("remove", request, allowCallback, cancelCallback);
     return request;
   },
 
   getRevision: function() {
     let request = this.createRequest();
 
     let allowCallback = function() {
       cpmm.sendAsyncMessage("Contacts:GetRevision", {
         requestID: this.getRequestId({ request: request })
       });
     }.bind(this);
 
-    let cancelCallback = function() {
-      Services.DOMRequest.fireError(request, "");
+    let cancelCallback = function(reason) {
+      Services.DOMRequest.fireErrorAsync(request, reason);
     };
 
     this.askPermission("revision", request, allowCallback, cancelCallback);
     return request;
   },
 
   getCount: function() {
     let request = this.createRequest();
 
     let allowCallback = function() {
       cpmm.sendAsyncMessage("Contacts:GetCount", {
         requestID: this.getRequestId({ request: request })
       });
     }.bind(this);
 
-    let cancelCallback = function() {
-      Services.DOMRequest.fireError(request, "");
+    let cancelCallback = function(reason) {
+      Services.DOMRequest.fireErrorAsync(request, reason);
     };
 
     this.askPermission("count", request, allowCallback, cancelCallback);
     return request;
   },
 
   init: function(aWindow) {
     // DOMRequestIpcHelper.initHelper sets this._window
--- a/dom/contacts/tests/mochitest.ini
+++ b/dom/contacts/tests/mochitest.ini
@@ -16,9 +16,9 @@ skip-if = (toolkit == 'gonk' && debug) #
 [test_contacts_international.html]
 [test_contacts_substringmatching.html]
 [test_contacts_substringmatchingVE.html]
 [test_contacts_substringmatchingCL.html]
 [test_migration.html]
   support-files =
     test_migration_chrome.js
   skip-if = os == "android"
-
+[test_permission_denied.html]
new file mode 100644
--- /dev/null
+++ b/dom/contacts/tests/test_permission_denied.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=674720
+-->
+<head>
+  <title>Test for Bug 1081873</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=1081873">Mozilla Bug 1081873</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="text/javascript;version=1.8" src="http://mochi.test:8888/tests/dom/contacts/tests/shared.js"></script>
+<script class="testbody" type="text/javascript">
+"use strict";
+
+SpecialPowers.removePermission("contacts-write", document);
+SpecialPowers.removePermission("contacts-read", document);
+SpecialPowers.removePermission("contacts-create", document);
+
+function onUnexpectedSuccess() {
+  ok(false, "Unexpected success");
+  next();
+}
+
+function onExpectedError(event) {
+  is(event.target.error.name, PERMISSION_DENIED, "Expected PERMISSION_DENIED");
+  next();
+}
+
+const PERMISSION_DENIED = "PERMISSION_DENIED";
+
+var steps = [
+  function() {
+    ok(true, "Add contact without permission");
+    var req = navigator.mozContacts.save(new mozContact({}));
+    req.onsuccess = onUnexpectedSuccess;
+    req.onerror = onExpectedError;
+  },
+  function() {
+    ok(true, "Find contact without permission");
+    var req = navigator.mozContacts.find({});
+    req.onsuccess = onUnexpectedSuccess;
+    req.onerror = onExpectedError;
+  },
+  function() {
+    ok(true, "Get all contacts without permission");
+    var req = navigator.mozContacts.getAll();
+    req.onsuccess = onUnexpectedSuccess;
+    req.onerror = onExpectedError;
+  },
+  function() {
+    ok(true, "Remove contact without permission");
+    var req = navigator.mozContacts.remove("aId");
+    req.onsuccess = onUnexpectedSuccess;
+    req.onerror = onExpectedError;
+  },
+  function() {
+    ok(true, "Clear contacts without permission");
+    var req = navigator.mozContacts.clear();
+    req.onsuccess = onUnexpectedSuccess;
+    req.onerror = onExpectedError;
+  },
+  function() {
+    ok(true, "Get revision without permission");
+    var req = navigator.mozContacts.getRevision();
+    req.onsuccess = onUnexpectedSuccess;
+    req.onerror = onExpectedError;
+  },
+  function() {
+    ok(true, "Get count without permission");
+    var req = navigator.mozContacts.getCount();
+    req.onsuccess = onUnexpectedSuccess;
+    req.onerror = function() {
+      is(req.error.name, PERMISSION_DENIED, "Expected PERMISSION_DENIED");
+      SimpleTest.finish();
+    };
+  }
+];
+
+start_tests();
+</script>
+</pre>
+</body>
+</html>