--- a/dom/contacts/ContactManager.js
+++ b/dom/contacts/ContactManager.js
@@ -170,26 +170,38 @@ Contact.prototype = {
flags: nsIClassInfo.DOM_OBJECT}),
QueryInterface : XPCOMUtils.generateQI([nsIDOMContact, nsIDOMContactProperties])
}
// 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()
{
debug("Constructor");
}
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;
if (this.hasPrivileges) {
debug("save: " + JSON.stringify(aContact) + " :" + aContact.id);
let newContact = {};
newContact.properties = {
name: [],
@@ -211,39 +223,43 @@ ContactManager.prototype = {
impp: [],
anniversary: null,
sex: null,
genderIdentity: null
};
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;
}
},
remove: function removeContact(aRecord) {
let request;
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;
}
},
_setMetaData: function(aNewContact, aRecord) {
aNewContact.id = aRecord.id;
@@ -268,60 +284,66 @@ ContactManager.prototype = {
let contacts = msg.contacts;
switch (aMessage.name) {
case "Contacts:Find:Return:OK":
let req = this.getRequest(msg.requestID);
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);
}
break;
case "Contact:Save:Return:OK":
case "Contacts:Clear:Return:OK":
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":
case "Contact:Remove:Return:KO":
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);
}
this.removeRequest(msg.requestID);
},
find: function(aOptions) {
let request;
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");
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
}
},
clear: function() {
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");
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
}
},
init: function(aWindow) {
@@ -341,19 +363,53 @@ ContactManager.prototype = {
Ci.nsIPermissionManager.ALLOW_ACTION :
Services.perms.testExactPermission(principal.URI, "webcontacts-manage");
//only pages with perm set can use the contacts
this.hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION;
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]),
classInfo : XPCOMUtils.generateCI({classID: CONTACTMANAGER_CID,
contractID: CONTACTMANAGER_CONTRACTID,
classDescription: "ContactManager",
interfaces: [nsIDOMContactManager],
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])
--- a/dom/contacts/ContactManager.manifest
+++ b/dom/contacts/ContactManager.manifest
@@ -6,11 +6,11 @@ contract @mozilla.org/contactAddress;1 {
component {e31daea0-0cb6-11e1-be50-0800200c9a66} ContactManager.js
contract @mozilla.org/contactFindOptions;1 {e31daea0-0cb6-11e1-be50-0800200c9a66}
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
--- a/dom/contacts/fallback/ContactService.jsm
+++ b/dom/contacts/fallback/ContactService.jsm
@@ -90,28 +90,35 @@ let DOMContactManager = {
result.sort(sortfunction);
if (msg.findOptions.filterLimit)
result = result.slice(0, msg.findOptions.filterLimit);
}
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)
+ );
}
}
}
DOMContactManager.init();
--- a/dom/contacts/tests/test_contacts_basics.html
+++ b/dom/contacts/tests/test_contacts_basics.html
@@ -226,16 +226,22 @@ var steps = [
req2.onerror = onFailure;
}
req.onerror = onFailure;
},
function () {
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.");
sample_id1 = createResult1.id;
checkContacts(properties1, createResult1);
next();
};
req.onerror = onFailure;
@@ -251,20 +257,144 @@ var steps = [
findResult1 = req.result[0];
ok(findResult1.id == sample_id1, "Same ID");
checkContacts(createResult1, properties1);
next();
};
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.");
findResult1 = req.result[0];
ok(findResult1.id == sample_id1, "Same ID");
checkContacts(createResult1, properties1);
next();
};
--- a/dom/interfaces/contacts/nsIDOMContactManager.idl
+++ b/dom/interfaces/contacts/nsIDOMContactManager.idl
@@ -1,33 +1,43 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "domstubs.idl"
#include "nsIDOMContactProperties.idl"
+#include "nsIDOMEvent.idl"
interface nsIArray;
interface nsIDOMContactFindOptions;
interface nsIDOMContactProperties;
interface nsIDOMDOMRequest;
[scriptable, uuid(da0f7040-388b-11e1-b86c-0800200c9a66)]
interface nsIDOMContact : nsIDOMContactProperties
{
attribute DOMString id;
readonly attribute jsval published;
readonly attribute jsval updated;
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);
nsIDOMDOMRequest clear();
nsIDOMDOMRequest save(in nsIDOMContact contact);
nsIDOMDOMRequest remove(in nsIDOMContact contact);
-};
\ No newline at end of file
+
+ attribute nsIDOMEventListener oncontactchange;
+};
--- a/dom/interfaces/contacts/nsIDOMContactProperties.idl
+++ b/dom/interfaces/contacts/nsIDOMContactProperties.idl
@@ -46,9 +46,9 @@ interface nsIDOMContactProperties : nsIS
attribute jsval tel; // DOMString[]
attribute jsval org; // DOMString[]
attribute jsval bday; // Date
attribute jsval note; // DOMString[]
attribute jsval impp; // DOMString[]
attribute jsval anniversary; // Date
attribute jsval sex; // DOMString
attribute jsval genderIdentity; // DOMString
-};
\ No newline at end of file
+};