From 756bf1141eda3223421a7b2f1d593c9cde269d23 Mon Sep 17 00:00:00 2001 From: Gregor Wagner Date: Tue, 8 May 2012 11:42:41 -0700 Subject: [PATCH] Bug 750768 - Contacts API: add DB modification event. r=fabrice --- dom/contacts/ContactManager.js | 74 ++++++++-- dom/contacts/ContactManager.manifest | 4 +- dom/contacts/fallback/ContactService.jsm | 23 ++-- dom/contacts/tests/test_contacts_basics.html | 130 ++++++++++++++++++ .../contacts/nsIDOMContactManager.idl | 14 +- .../contacts/nsIDOMContactProperties.idl | 2 +- 6 files changed, 225 insertions(+), 22 deletions(-) diff --git a/dom/contacts/ContactManager.js b/dom/contacts/ContactManager.js index cb96e0ca76e6..637584039d3e 100644 --- a/dom/contacts/ContactManager.js +++ b/dom/contacts/ContactManager.js @@ -175,7 +175,7 @@ Contact.prototype = { // ContactManager const CONTACTMANAGER_CONTRACTID = "@mozilla.org/contactManager;1"; -const CONTACTMANAGER_CID = Components.ID("{50a820b0-ced0-11e0-9572-0800200c9a66}"); +const CONTACTMANAGER_CID = Components.ID("{d9ca0950-93d1-11e1-b0c4-0800200c9a66}"); const nsIDOMContactManager = Components.interfaces.nsIDOMContactManager; function ContactManager() @@ -185,6 +185,18 @@ function ContactManager() ContactManager.prototype = { __proto__: DOMRequestIpcHelper.prototype, + _oncontactchange: null, + + set oncontactchange(aCallback) { + if (this.hasPrivileges) + this._oncontactchange = aCallback; + else + throw Components.results.NS_ERROR_FAILURE; + }, + + get oncontactchange() { + return this._oncontactchange; + }, save: function save(aContact) { let request; @@ -216,17 +228,21 @@ ContactManager.prototype = { for (let field in newContact.properties) newContact.properties[field] = aContact[field]; + let reason; if (aContact.id == "undefined") { // for example {25c00f01-90e5-c545-b4d4-21E2ddbab9e0} becomes // 25c00f0190e5c545b4d421E2ddbab9e0 - aContact.id = this._getRandomId().replace('-', '').replace('{', '').replace('}', ''); + aContact.id = this._getRandomId().replace('-', '', 'g').replace('{', '').replace('}', ''); + reason = "create"; + } else { + reason = "update"; } this._setMetaData(newContact, aContact); debug("send: " + JSON.stringify(newContact)); request = this.createRequest(); cpmm.sendAsyncMessage("Contact:Save", {contact: newContact, - requestID: this.getRequestId(request)}); + requestID: this.getRequestId({request: request, reason: reason })}); return request; } else { throw Components.results.NS_ERROR_NOT_IMPLEMENTED; @@ -238,7 +254,7 @@ ContactManager.prototype = { if (this.hasPrivileges) { request = this.createRequest(); cpmm.sendAsyncMessage("Contact:Remove", {id: aRecord.id, - requestID: this.getRequestId(request)}); + requestID: this.getRequestId({request: request, reason: "remove"})}); return request; } else { throw Components.results.NS_ERROR_NOT_IMPLEMENTED; @@ -273,7 +289,7 @@ ContactManager.prototype = { if (req) { let result = this._convertContactsArray(contacts); debug("result: " + JSON.stringify(result)); - Services.DOMRequest.fireSuccess(req, result); + Services.DOMRequest.fireSuccess(req.request, result); } else { debug("no request stored!" + msg.requestID); } @@ -283,7 +299,13 @@ ContactManager.prototype = { case "Contact:Remove:Return:OK": req = this.getRequest(msg.requestID); if (req) - Services.DOMRequest.fireSuccess(req, null); + Services.DOMRequest.fireSuccess(req.request, null); + + // Fire oncontactchange event + if (this._oncontactchange) { + let event = new MozContactEvent(msg.contactID, req.reason); + this._oncontactchange.handleEvent(event); + } break; case "Contacts:Find:Return:KO": case "Contact:Save:Return:KO": @@ -291,7 +313,7 @@ ContactManager.prototype = { case "Contacts:Clear:Return:KO": req = this.getRequest(msg.requestID); if (req) - Services.DOMRequest.fireError(req, msg.errorMsg); + Services.DOMRequest.fireError(req.request, msg.errorMsg); break; default: debug("Wrong message: " + aMessage.name); @@ -304,7 +326,7 @@ ContactManager.prototype = { if (this.hasPrivileges) { request = this.createRequest(); cpmm.sendAsyncMessage("Contacts:Find", {findOptions: aOptions, - requestID: this.getRequestId(request)}); + requestID: this.getRequestId({request: request, reason: "find"})}); return request; } else { debug("find not allowed"); @@ -316,7 +338,7 @@ ContactManager.prototype = { let request; if (this.hasPrivileges) { request = this.createRequest(); - cpmm.sendAsyncMessage("Contacts:Clear", {requestID: this.getRequestId(request)}); + cpmm.sendAsyncMessage("Contacts:Clear", {requestID: this.getRequestId({request: request, reason: "remove"})}); return request; } else { debug("clear not allowed"); @@ -346,6 +368,13 @@ ContactManager.prototype = { debug("has privileges :" + this.hasPrivileges); }, + // Called from DOMRequestIpcHelper + uninit: function uninit() { + debug("uninit call"); + if (this._oncontactchange) + this._oncontactchange = null; + }, + classID : CONTACTMANAGER_CID, QueryInterface : XPCOMUtils.generateQI([nsIDOMContactManager, Ci.nsIDOMGlobalPropertyInitializer]), @@ -356,4 +385,31 @@ ContactManager.prototype = { flags: nsIClassInfo.DOM_OBJECT}) } +// MozContactEvent object +function MozContactEvent(aContactID, aReason) { + debug("ContactEventConstr: " + aContactID + ", " + aReason); + this._contactID = aContactID; + this._reason = aReason; +} + +MozContactEvent.prototype = { + get contactID() { + return this._contactID; + }, + + get reason() { + return this._reason; + }, + + classID: Components.ID("{a8cd4ba0-93d1-11e1-b0c4-0800200c9a66}"), + + QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMContactEvent]), + + classInfo: XPCOMUtils.generateCI({classID: Components.ID("{a8cd4ba0-93d1-11e1-b0c4-0800200c9a66}"), + contractID: "@mozilla.org/contact-event;1", + interfaces: [Ci.mozIDOMContactEvent], + flags: Ci.nsIClassInfo.DOM_OBJECT, + classDescription: "Contact Change Event"}) +} + const NSGetFactory = XPCOMUtils.generateNSGetFactory([Contact, ContactManager, ContactProperties, ContactAddress, ContactFindOptions]) diff --git a/dom/contacts/ContactManager.manifest b/dom/contacts/ContactManager.manifest index 01929e20c96a..30bf269a3ce9 100644 --- a/dom/contacts/ContactManager.manifest +++ b/dom/contacts/ContactManager.manifest @@ -11,6 +11,6 @@ component {da0f7040-388b-11e1-b86c-0800200c9a66} ContactManager.js contract @mozilla.org/contact;1 {da0f7040-388b-11e1-b86c-0800200c9a66} category JavaScript-global-constructor mozContact @mozilla.org/contact;1 -component {50a820b0-ced0-11e0-9572-0800200c9a66} ContactManager.js -contract @mozilla.org/contactManager;1 {50a820b0-ced0-11e0-9572-0800200c9a66} +component {d9ca0950-93d1-11e1-b0c4-0800200c9a66} ContactManager.js +contract @mozilla.org/contactManager;1 {d9ca0950-93d1-11e1-b0c4-0800200c9a66} category JavaScript-navigator-property mozContacts @mozilla.org/contactManager;1 diff --git a/dom/contacts/fallback/ContactService.jsm b/dom/contacts/fallback/ContactService.jsm index 5f8f8bee9e50..de68eaafc001 100644 --- a/dom/contacts/fallback/ContactService.jsm +++ b/dom/contacts/fallback/ContactService.jsm @@ -95,21 +95,28 @@ let DOMContactManager = { debug("result:" + JSON.stringify(result)); ppmm.sendAsyncMessage("Contacts:Find:Return:OK", {requestID: msg.requestID, contacts: result}); }.bind(this), - function(aErrorMsg) { ppmm.sendAsyncMessage("Contacts:Find:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }) }.bind(this), + function(aErrorMsg) { ppmm.sendAsyncMessage("Contacts:Find:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }) }.bind(this), msg.findOptions); break; case "Contact:Save": - this._db.saveContact(msg.contact, function() { ppmm.sendAsyncMessage("Contact:Save:Return:OK", { requestID: msg.requestID }); }.bind(this), - function(aErrorMsg) { ppmm.sendAsyncMessage("Contact:Save:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this)); + this._db.saveContact( + msg.contact, + function() { ppmm.sendAsyncMessage("Contact:Save:Return:OK", { requestID: msg.requestID, contactID: msg.contact.id }); }.bind(this), + function(aErrorMsg) { ppmm.sendAsyncMessage("Contact:Save:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this) + ); break; case "Contact:Remove": - this._db.removeContact(msg.id, - function() { ppmm.sendAsyncMessage("Contact:Remove:Return:OK", { requestID: msg.requestID }); }.bind(this), - function(aErrorMsg) { ppmm.sendAsyncMessage("Contact:Remove:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this)); + this._db.removeContact( + msg.id, + function() { ppmm.sendAsyncMessage("Contact:Remove:Return:OK", { requestID: msg.requestID, contactID: msg.id }); }.bind(this), + function(aErrorMsg) { ppmm.sendAsyncMessage("Contact:Remove:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this) + ); break; case "Contacts:Clear": - this._db.clear(function() { ppmm.sendAsyncMessage("Contacts:Clear:Return:OK", { requestID: msg.requestID }); }.bind(this), - function(aErrorMsg) { ppmm.sendAsyncMessage("Contacts:Clear:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this)); + this._db.clear( + function() { ppmm.sendAsyncMessage("Contacts:Clear:Return:OK", { requestID: msg.requestID }); }.bind(this), + function(aErrorMsg) { ppmm.sendAsyncMessage("Contacts:Clear:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this) + ); } } } diff --git a/dom/contacts/tests/test_contacts_basics.html b/dom/contacts/tests/test_contacts_basics.html index 066c0db099ce..c44c0344a662 100644 --- a/dom/contacts/tests/test_contacts_basics.html +++ b/dom/contacts/tests/test_contacts_basics.html @@ -231,6 +231,12 @@ var steps = [ ok(true, "Adding a new contact1"); createResult1 = new mozContact(); createResult1.init(properties1); + + mozContacts.oncontactchange = function(event) { + is(event.contactID, createResult1.id, "Same contactID"); + is(event.reason, "create", "Same reason"); + } + req = navigator.mozContacts.save(createResult1); req.onsuccess = function () { ok(createResult1.id, "The contact now has an ID."); @@ -255,11 +261,135 @@ var steps = [ }; req.onerror = onFailure; }, + function () { + ok(true, "Retrieving by substring and update"); + mozContacts.oncontactchange = function(event) { + is(event.contactID, findResult1.id, "Same contactID"); + is(event.reason, "update", "Same reason"); + } + var options = {filterBy: ["name"], + filterOp: "contains", + filterValue: properties1.name.substring(0,3)}; + req = mozContacts.find(options); + req.onsuccess = function () { + ok(req.result.length == 1, "Found exactly 1 contact."); + findResult1 = req.result[0]; + findResult1.jobTitle = ["new Job"]; + ok(findResult1.id == sample_id1, "Same ID"); + checkContacts(createResult1, properties1); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Adding a new contact"); + mozContacts.oncontactchange = function(event) { + is(event.contactID, createResult2.id, "Same contactID"); + is(event.reason, "create", "Same reason"); + } + createResult2 = new mozContact(); + createResult2.init({name: "newName"}); + req = navigator.mozContacts.save(createResult2); + req.onsuccess = function () { + ok(createResult2.id, "The contact now has an ID."); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Retrieving by substring"); + var options = {filterBy: ["name"], + filterOp: "contains", + filterValue: properties1.name.substring(0,3)}; + req = mozContacts.find(options); + req.onsuccess = function () { + ok(req.result.length == 1, "Found exactly 1 contact."); + findResult1 = req.result[0]; + checkContacts(createResult1, findResult1); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Remove contact1"); + mozContacts.oncontactchange = function(event) { + is(event.contactID, createResult1.id, "Same contactID"); + is(event.reason, "remove", "Same reason"); + } + req = navigator.mozContacts.remove(createResult1); + req.onsuccess = function () { + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Retrieving by substring"); + var options = {filterBy: ["name"], + filterOp: "contains", + filterValue: properties1.name.substring(0,3)}; + req = mozContacts.find(options); + req.onsuccess = function () { + ok(req.result.length == 0, "Found no contact."); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Remove contact2"); + mozContacts.oncontactchange = function(event) { + is(event.contactID, createResult2.id, "Same contactID"); + is(event.reason, "remove", "Same reason"); + } + req = navigator.mozContacts.remove(createResult2); + req.onsuccess = function () { + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Retrieving by substring"); + var options = {filterBy: ["name"], + filterOp: "contains", + filterValue: properties1.name.substring(0,3)}; + req = mozContacts.find(options); + req.onsuccess = function () { + ok(req.result.length == 0, "Found no contact."); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Deleting database"); + mozContacts.oncontactchange = function(event) { + is(event.contactID, "undefined", "Same contactID"); + is(event.reason, "remove", "Same reason"); + } + req = mozContacts.clear(); + req.onsuccess = function () { + ok(true, "Deleted the database"); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Adding a new contact with properties1"); + createResult1 = new mozContact(); + createResult1.init(properties1); + req = navigator.mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + sample_id1 = createResult1.id; + checkContacts(properties1, createResult1); + next(); + }; + req.onerror = onFailure; + }, function () { ok(true, "Retrieving by substring tel1"); var options = {filterBy: ["tel"], filterOp: "contains", filterValue: properties1.tel[1].substring(1,5)}; + mozContacts.oncontactchange = null; req = mozContacts.find(options); req.onsuccess = function () { ok(req.result.length == 1, "Found exactly 1 contact."); diff --git a/dom/interfaces/contacts/nsIDOMContactManager.idl b/dom/interfaces/contacts/nsIDOMContactManager.idl index 90c650159f2b..1ae86e399998 100644 --- a/dom/interfaces/contacts/nsIDOMContactManager.idl +++ b/dom/interfaces/contacts/nsIDOMContactManager.idl @@ -4,6 +4,7 @@ #include "domstubs.idl" #include "nsIDOMContactProperties.idl" +#include "nsIDOMEvent.idl" interface nsIArray; interface nsIDOMContactFindOptions; @@ -20,7 +21,14 @@ interface nsIDOMContact : nsIDOMContactProperties void init(in nsIDOMContactProperties properties); // Workaround BUG 723206 }; -[scriptable, uuid(50a820b0-ced0-11e0-9572-0800200c9a66)] +[scriptable, uuid(a8cd4ba0-93d1-11e1-b0c4-0800200c9a66)] +interface mozIDOMContactEvent : nsIDOMEvent +{ + readonly attribute DOMString contactID; + readonly attribute DOMString reason; +}; + +[scriptable, uuid(d9ca0950-93d1-11e1-b0c4-0800200c9a66)] interface nsIDOMContactManager : nsISupports { nsIDOMDOMRequest find(in nsIDOMContactFindOptions options); @@ -30,4 +38,6 @@ interface nsIDOMContactManager : nsISupports nsIDOMDOMRequest save(in nsIDOMContact contact); nsIDOMDOMRequest remove(in nsIDOMContact contact); -}; \ No newline at end of file + + attribute nsIDOMEventListener oncontactchange; +}; diff --git a/dom/interfaces/contacts/nsIDOMContactProperties.idl b/dom/interfaces/contacts/nsIDOMContactProperties.idl index f1ae12812865..7cf52ea1d781 100644 --- a/dom/interfaces/contacts/nsIDOMContactProperties.idl +++ b/dom/interfaces/contacts/nsIDOMContactProperties.idl @@ -51,4 +51,4 @@ interface nsIDOMContactProperties : nsISupports attribute jsval anniversary; // Date attribute jsval sex; // DOMString attribute jsval genderIdentity; // DOMString -}; \ No newline at end of file +};