Bug 850430 - Convert the Contacts API to WebIDL. r=bz r=gwagner

This commit is contained in:
Reuben Morais 2013-09-29 22:22:34 -03:00
parent a7d36118a0
commit e3d4aa1b0f
19 changed files with 876 additions and 1017 deletions

View File

@ -15,9 +15,9 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
XPCOMUtils.defineLazyGetter(Services, "DOMRequest", function() {
return Cc["@mozilla.org/dom/dom-request-service;1"].getService(Ci.nsIDOMRequestService);
});
XPCOMUtils.defineLazyServiceGetter(Services, "DOMRequest",
"@mozilla.org/dom/dom-request-service;1",
"nsIDOMRequestService");
XPCOMUtils.defineLazyServiceGetter(this, "pm",
"@mozilla.org/permissionmanager;1",
@ -29,325 +29,196 @@ XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
const CONTACTS_SENDMORE_MINIMUM = 5;
function stringOrBust(aObj) {
if (typeof aObj != "string") {
if (DEBUG) debug("Field is not a string and was ignored.");
return undefined;
} else {
return aObj;
}
}
function sanitizeStringArray(aArray) {
if (!Array.isArray(aArray)) {
aArray = [aArray];
}
return aArray.map(stringOrBust).filter(function(el) { return el != undefined; });
}
const nsIClassInfo = Ci.nsIClassInfo;
const CONTACTPROPERTIES_CID = Components.ID("{35ad8a4e-9486-44b6-883d-550f14635e49}");
const nsIContactProperties = Ci.nsIContactProperties;
// ContactProperties is not directly instantiated. It is used as interface.
function ContactProperties(aProp) { if (DEBUG) debug("ContactProperties Constructor"); }
ContactProperties.prototype = {
classID : CONTACTPROPERTIES_CID,
classInfo : XPCOMUtils.generateCI({classID: CONTACTPROPERTIES_CID,
contractID:"@mozilla.org/contactProperties;1",
classDescription: "ContactProperties",
interfaces: [nsIContactProperties],
flags: nsIClassInfo.DOM_OBJECT}),
QueryInterface : XPCOMUtils.generateQI([nsIContactProperties])
}
//ContactAddress
const CONTACTADDRESS_CONTRACTID = "@mozilla.org/contactAddress;1";
const CONTACTADDRESS_CID = Components.ID("{9cbfa81c-bcab-4ca9-b0d2-f4318f295e33}");
const nsIContactAddress = Components.interfaces.nsIContactAddress;
function ContactAddress(aType, aStreetAddress, aLocality, aRegion, aPostalCode, aCountryName, aPref) {
this.type = sanitizeStringArray(aType);
this.streetAddress = stringOrBust(aStreetAddress);
this.locality = stringOrBust(aLocality);
this.region = stringOrBust(aRegion);
this.postalCode = stringOrBust(aPostalCode);
this.countryName = stringOrBust(aCountryName);
this.pref = aPref;
};
function ContactAddress() { }
ContactAddress.prototype = {
__exposedProps__: {
type: 'rw',
streetAddress: 'rw',
locality: 'rw',
region: 'rw',
postalCode: 'rw',
countryName: 'rw',
pref: 'rw'
},
// This function is meant to be called via bindings code for type checking,
// don't call it directly. Instead, create a content object and call initialize
// on that.
initialize: function(aType, aStreetAddress, aLocality, aRegion, aPostalCode, aCountryName, aPref) {
this.type = aType;
this.streetAddress = aStreetAddress;
this.locality = aLocality;
this.region = aRegion;
this.postalCode = aPostalCode;
this.countryName = aCountryName;
this.pref = aPref;
},
classID : CONTACTADDRESS_CID,
classInfo : XPCOMUtils.generateCI({classID: CONTACTADDRESS_CID,
contractID: CONTACTADDRESS_CONTRACTID,
classDescription: "ContactAddress",
interfaces: [nsIContactAddress],
flags: nsIClassInfo.DOM_OBJECT}),
toJSON: function(excludeExposedProps) {
let json = {
type: this.type,
streetAddress: this.streetAddress,
locality: this.locality,
region: this.region,
postalCode: this.postalCode,
countryName: this.countryName,
pref: this.pref,
};
if (!excludeExposedProps) {
json.__exposedProps__ = {
type: "rw",
streetAddress: "rw",
locality: "rw",
region: "rw",
postalCode: "rw",
countryName: "rw",
pref: "rw",
};
}
return json;
},
QueryInterface : XPCOMUtils.generateQI([nsIContactAddress])
}
//ContactField
const CONTACTFIELD_CONTRACTID = "@mozilla.org/contactField;1";
const CONTACTFIELD_CID = Components.ID("{ad19a543-69e4-44f0-adfa-37c011556bc1}");
const nsIContactField = Components.interfaces.nsIContactField;
function ContactField(aType, aValue, aPref) {
this.type = sanitizeStringArray(aType);
this.value = stringOrBust(aValue);
this.pref = aPref;
classID: Components.ID("{9cbfa81c-bcab-4ca9-b0d2-f4318f295e33}"),
contractID: "@mozilla.org/contactAddress;1",
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
};
function ContactField() { }
ContactField.prototype = {
__exposedProps__: {
type: 'rw',
value: 'rw',
pref: 'rw'
},
// This function is meant to be called via bindings code for type checking,
// don't call it directly. Instead, create a content object and call initialize
// on that.
initialize: function(aType, aValue, aPref) {
this.type = aType;
this.value = aValue;
this.pref = aPref;
},
classID : CONTACTFIELD_CID,
classInfo : XPCOMUtils.generateCI({classID: CONTACTFIELD_CID,
contractID: CONTACTFIELD_CONTRACTID,
classDescription: "ContactField",
interfaces: [nsIContactField],
flags: nsIClassInfo.DOM_OBJECT}),
toJSON: function(excludeExposedProps) {
let json = {
type: this.type,
value: this.value,
pref: this.pref,
};
if (!excludeExposedProps) {
json.__exposedProps__ = {
type: "rw",
value: "rw",
pref: "rw",
};
}
return json;
},
QueryInterface : XPCOMUtils.generateQI([nsIContactField])
}
//ContactTelField
const CONTACTTELFIELD_CONTRACTID = "@mozilla.org/contactTelField;1";
const CONTACTTELFIELD_CID = Components.ID("{4d42c5a9-ea5d-4102-80c3-40cc986367ca}");
const nsIContactTelField = Components.interfaces.nsIContactTelField;
function ContactTelField(aType, aValue, aCarrier, aPref) {
this.type = sanitizeStringArray(aType);
this.value = stringOrBust(aValue);
this.carrier = stringOrBust(aCarrier);
this.pref = aPref;
classID: Components.ID("{ad19a543-69e4-44f0-adfa-37c011556bc1}"),
contractID: "@mozilla.org/contactField;1",
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
};
function ContactTelField() { }
ContactTelField.prototype = {
__exposedProps__: {
type: 'rw',
value: 'rw',
carrier: 'rw',
pref: 'rw'
},
// This function is meant to be called via bindings code for type checking,
// don't call it directly. Instead, create a content object and call initialize
// on that.
initialize: function(aType, aValue, aCarrier, aPref) {
this.type = aType;
this.value = aValue;
this.carrier = aCarrier;
this.pref = aPref;
},
classID : CONTACTTELFIELD_CID,
classInfo : XPCOMUtils.generateCI({classID: CONTACTTELFIELD_CID,
contractID: CONTACTTELFIELD_CONTRACTID,
classDescription: "ContactTelField",
interfaces: [nsIContactTelField],
flags: nsIClassInfo.DOM_OBJECT}),
toJSON: function(excludeExposedProps) {
let json = {
type: this.type,
value: this.value,
carrier: this.carrier,
pref: this.pref,
};
if (!excludeExposedProps) {
json.__exposedProps__ = {
type: "rw",
value: "rw",
carrier: "rw",
pref: "rw",
};
}
return json;
},
QueryInterface : XPCOMUtils.generateQI([nsIContactTelField])
}
//ContactFindSortOptions
const CONTACTFINDSORTOPTIONS_CONTRACTID = "@mozilla.org/contactFindSortOptions;1"
const CONTACTFINDSORTOPTIONS_CID = Components.ID("{0a5b1fab-70da-46dd-b902-619904d920c2}");
const nsIContactFindSortOptions = Ci.nsIContactFindSortOptions;
function ContactFindSortOptions () { }
ContactFindSortOptions.prototype = {
classID: CONTACTFINDSORTOPTIONS_CID,
classInfo: XPCOMUtils.generateCI({classID: CONTACTFINDSORTOPTIONS_CID,
contractID: CONTACTFINDSORTOPTIONS_CONTRACTID,
classDescription: "ContactFindSortOptions",
interfaces: [nsIContactFindSortOptions],
flags: nsIClassInfo.DOM_OBJECT}),
QueryInterface: XPCOMUtils.generateQI([nsIContactFindSortOptions])
classID: Components.ID("{4d42c5a9-ea5d-4102-80c3-40cc986367ca}"),
contractID: "@mozilla.org/contactTelField;1",
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
};
//ContactFindOptions
function validateArrayField(data, createCb) {
function isVanillaObj(aObj) {
return Object.prototype.toString.call(aObj) == "[object Object]";
}
const CONTACTFINDOPTIONS_CONTRACTID = "@mozilla.org/contactFindOptions;1";
const CONTACTFINDOPTIONS_CID = Components.ID("{28ce07d0-45d9-4b7a-8843-521df4edd8bc}");
const nsIContactFindOptions = Components.interfaces.nsIContactFindOptions;
// We use an array-like Proxy to validate data set by content, since we don't
// have WebIDL arrays yet. See bug 851726.
function ContactFindOptions() { };
ContactFindOptions.prototype = {
classID : CONTACTFINDOPTIONS_CID,
classInfo : XPCOMUtils.generateCI({classID: CONTACTFINDOPTIONS_CID,
contractID: CONTACTFINDOPTIONS_CONTRACTID,
classDescription: "ContactFindOptions",
interfaces: [nsIContactFindSortOptions,
nsIContactFindOptions],
flags: nsIClassInfo.DOM_OBJECT}),
QueryInterface : XPCOMUtils.generateQI([nsIContactFindSortOptions,
nsIContactFindOptions])
}
//Contact
const CONTACT_CONTRACTID = "@mozilla.org/contact;1";
const CONTACT_CID = Components.ID("{72a5ee28-81d8-4af8-90b3-ae935396cc66}");
const nsIDOMContact = Components.interfaces.nsIDOMContact;
function checkBlobArray(aBlob) {
if (Array.isArray(aBlob)) {
for (let i = 0; i < aBlob.length; i++) {
if (typeof aBlob != 'object') {
return null;
// ArrayPropertyExposedPropsProxy is used to return "rw" for any valid index
// and "length" in __exposedProps__.
const ArrayPropertyExposedPropsProxy = new Proxy({}, {
get: function(target, name) {
// Test for index access
if (String(name >>> 0) === name) {
return "rw";
}
if (!(aBlob[i] instanceof Components.interfaces.nsIDOMBlob)) {
return null;
if (name === "length") {
return "r";
}
}
return aBlob;
}
return null;
}
});
function isVanillaObj(aObj) {
return Object.prototype.toString.call(aObj) == "[object Object]";
}
const ArrayPropertyHandler = {
set: function(target, name, val, receiver) {
// Test for index access
if (String(name >>> 0) === name) {
target[name] = createCb(val);
}
},
get: function(target, name) {
if (name === "__exposedProps__") {
return ArrayPropertyExposedPropsProxy;
}
return target[name];
}
};
function validateArrayField(data, createCb) {
if (data) {
data = Array.isArray(data) ? data : [data];
let filtered = [];
for (let obj of data) {
if (obj && isVanillaObj(obj)) {
if (isVanillaObj(obj)) {
filtered.push(createCb(obj));
}
}
return filtered;
if (filtered.length === 0) {
return undefined;
}
return new Proxy(filtered, ArrayPropertyHandler);
}
return undefined;
}
function Contact() { };
// We need this to create a copy of the mozContact object in ContactManager.save
// Keep in sync with the interfaces.
const PROPERTIES = [
"name", "honorificPrefix", "givenName", "additionalName", "familyName",
"honorificSuffix", "nickname", "photo", "category", "org", "jobTitle",
"bday", "note", "anniversary", "sex", "genderIdentity", "key"
];
const ADDRESS_PROPERTIES = ["adr"];
const FIELD_PROPERTIES = ["email", "url", "impp"];
const TELFIELD_PROPERTIES = ["tel"];
function Contact() { }
Contact.prototype = {
__exposedProps__: {
id: 'rw',
updated: 'rw',
published: 'rw',
name: 'rw',
honorificPrefix: 'rw',
givenName: 'rw',
additionalName: 'rw',
familyName: 'rw',
honorificSuffix: 'rw',
nickname: 'rw',
email: 'rw',
photo: 'rw',
url: 'rw',
category: 'rw',
adr: 'rw',
tel: 'rw',
org: 'rw',
jobTitle: 'rw',
bday: 'rw',
note: 'rw',
impp: 'rw',
anniversary: 'rw',
sex: 'rw',
genderIdentity: 'rw',
key: 'rw',
},
set name(aName) {
this._name = sanitizeStringArray(aName);
},
get name() {
return this._name;
},
set honorificPrefix(aHonorificPrefix) {
this._honorificPrefix = sanitizeStringArray(aHonorificPrefix);
},
get honorificPrefix() {
return this._honorificPrefix;
},
set givenName(aGivenName) {
this._givenName = sanitizeStringArray(aGivenName);
},
get givenName() {
return this._givenName;
},
set additionalName(aAdditionalName) {
this._additionalName = sanitizeStringArray(aAdditionalName);
},
get additionalName() {
return this._additionalName;
},
set familyName(aFamilyName) {
this._familyName = sanitizeStringArray(aFamilyName);
},
get familyName() {
return this._familyName;
},
set honorificSuffix(aHonorificSuffix) {
this._honorificSuffix = sanitizeStringArray(aHonorificSuffix);
},
get honorificSuffix() {
return this._honorificSuffix;
},
set nickname(aNickname) {
this._nickname = sanitizeStringArray(aNickname);
},
get nickname() {
return this._nickname;
},
set photo(aPhoto) {
this._photo = checkBlobArray(aPhoto);
},
get photo() {
return this._photo;
},
set category(aCategory) {
this._category = sanitizeStringArray(aCategory);
},
get category() {
return this._category;
},
// We need to create the content interfaces in these setters, otherwise when
// we return these objects (e.g. from a find call), the values in the array
// will be COW's, and content cannot see the properties.
set email(aEmail) {
this._email = validateArrayField(aEmail, function(email) {
return new ContactField(email.type, email.value, email.pref);
});
let obj = new this._window.ContactField();
obj.initialize(email.type, email.value, email.pref);
return obj;
}.bind(this));
},
get email() {
@ -356,10 +227,12 @@ Contact.prototype = {
set adr(aAdr) {
this._adr = validateArrayField(aAdr, function(adr) {
return new ContactAddress(adr.type, adr.streetAddress, adr.locality,
adr.region, adr.postalCode, adr.countryName,
adr.pref);
});
let obj = new this._window.ContactAddress();
obj.initialize(adr.type, adr.streetAddress, adr.locality,
adr.region, adr.postalCode, adr.countryName,
adr.pref);
return obj;
}.bind(this));
},
get adr() {
@ -368,8 +241,10 @@ Contact.prototype = {
set tel(aTel) {
this._tel = validateArrayField(aTel, function(tel) {
return new ContactTelField(tel.type, tel.value, tel.carrier, tel.pref);
});
let obj = new this._window.ContactTelField();
obj.initialize(tel.type, tel.value, tel.carrier, tel.pref);
return obj;
}.bind(this));
},
get tel() {
@ -378,8 +253,10 @@ Contact.prototype = {
set impp(aImpp) {
this._impp = validateArrayField(aImpp, function(impp) {
return new ContactField(impp.type, impp.value, impp.pref);
});
let obj = new this._window.ContactField();
obj.initialize(impp.type, impp.value, impp.pref);
return obj;
}.bind(this));
},
get impp() {
@ -388,95 +265,21 @@ Contact.prototype = {
set url(aUrl) {
this._url = validateArrayField(aUrl, function(url) {
return new ContactField(url.type, url.value, url.pref);
});
let obj = new this._window.ContactField();
obj.initialize(url.type, url.value, url.pref);
return obj;
}.bind(this));
},
get url() {
return this._url;
},
set org(aOrg) {
this._org = sanitizeStringArray(aOrg);
init: function(aWindow) {
this._window = aWindow;
},
get org() {
return this._org;
},
set jobTitle(aJobTitle) {
this._jobTitle = sanitizeStringArray(aJobTitle);
},
get jobTitle() {
return this._jobTitle;
},
set note(aNote) {
this._note = sanitizeStringArray(aNote);
},
get note() {
return this._note;
},
set bday(aBday) {
if (aBday && aBday.constructor.name === "Date") {
this._bday = aBday;
} else if (typeof aBday === "string" || typeof aBday === "number") {
this._bday = new Date(aBday);
}
},
get bday() {
return this._bday;
},
set anniversary(aAnniversary) {
if (aAnniversary && aAnniversary.constructor.name === "Date") {
this._anniversary = aAnniversary;
} else if (typeof aAnniversary === "string" || typeof aAnniversary === "number") {
this._anniversary = new Date(aAnniversary);
}
},
get anniversary() {
return this._anniversary;
},
set sex(aSex) {
if (aSex !== "undefined") {
this._sex = aSex;
} else {
this._sex = null;
}
},
get sex() {
return this._sex;
},
set genderIdentity(aGenderIdentity) {
if (aGenderIdentity !== "undefined") {
this._genderIdentity = aGenderIdentity;
} else {
this._genderIdentity = null;
}
},
get genderIdentity() {
return this._genderIdentity;
},
set key(aKey) {
this._key = sanitizeStringArray(aKey);
},
get key() {
return this._key;
},
init: function init(aProp) {
__init: function(aProp) {
this.name = aProp.name;
this.honorificPrefix = aProp.honorificPrefix;
this.givenName = aProp.givenName;
@ -501,76 +304,105 @@ Contact.prototype = {
this.key = aProp.key;
},
get published () {
return this._published;
setMetadata: function(aId, aPublished, aUpdated) {
this.id = aId;
if (aPublished) {
this.published = aPublished;
}
if (aUpdated) {
this.updated = aUpdated;
}
},
set published(aPublished) {
this._published = aPublished;
toJSON: function() {
return {
id: this.id,
published: this.published,
updated: this.updated,
name: this.name,
honorificPrefix: this.honorificPrefix,
givenName: this.givenName,
additionalName: this.additionalName,
familyName: this.familyName,
honorificSuffix: this.honorificSuffix,
nickname: this.nickname,
category: this.category,
org: this.org,
jobTitle: this.jobTitle,
note: this.note,
sex: this.sex,
genderIdentity: this.genderIdentity,
email: this.email,
photo: this.photo,
adr: this.adr,
url: this.url,
tel: this.tel,
bday: this.bday,
impp: this.impp,
anniversary: this.anniversary,
key: this.key,
__exposedProps__: {
id: "rw",
published: "rw",
updated: "rw",
name: "rw",
honorificPrefix: "rw",
givenName: "rw",
additionalName: "rw",
familyName: "rw",
honorificSuffix: "rw",
nickname: "rw",
category: "rw",
org: "rw",
jobTitle: "rw",
note: "rw",
sex: "rw",
genderIdentity: "rw",
email: "rw",
photo: "rw",
adr: "rw",
url: "rw",
tel: "rw",
bday: "rw",
impp: "rw",
anniversary: "rw",
key: "rw",
}
};
},
get updated () {
return this._updated;
},
classID: Components.ID("{72a5ee28-81d8-4af8-90b3-ae935396cc66}"),
contractID: "@mozilla.org/contact;1",
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
Ci.nsIDOMGlobalPropertyInitializer]),
};
set updated(aUpdated) {
this._updated = aUpdated;
},
classID : CONTACT_CID,
classInfo : XPCOMUtils.generateCI({classID: CONTACT_CID,
contractID: CONTACT_CONTRACTID,
classDescription: "Contact",
interfaces: [nsIDOMContact, nsIContactProperties],
flags: nsIClassInfo.DOM_OBJECT}),
QueryInterface : XPCOMUtils.generateQI([nsIDOMContact, nsIContactProperties])
}
// ContactManager
const CONTACTMANAGER_CONTRACTID = "@mozilla.org/contactManager;1";
const CONTACTMANAGER_CID = Components.ID("{8beb3a66-d70a-4111-b216-b8e995ad3aff}");
const nsIDOMContactManager = Components.interfaces.nsIDOMContactManager;
function ContactManager()
{
if (DEBUG) debug("Constructor");
}
function ContactManager() { }
ContactManager.prototype = {
__proto__: DOMRequestIpcHelper.prototype,
_oncontactchange: null,
hasListenPermission: false,
_cachedContacts: [] ,
set oncontactchange(aCallback) {
if (DEBUG) debug("set oncontactchange");
let allowCallback = function() {
if (!this._oncontactchange) {
cpmm.sendAsyncMessage("Contacts:RegisterForMessages");
}
this._oncontactchange = aCallback;
}.bind(this);
let cancelCallback = function() {
throw Components.results.NS_ERROR_FAILURE;
}
this.askPermission("listen", null, allowCallback, cancelCallback);
set oncontactchange(aHandler) {
this.__DOM_IMPL__.setEventHandler("oncontactchange", aHandler);
},
get oncontactchange() {
return this._oncontactchange;
return this.__DOM_IMPL__.getEventHandler("oncontactchange");
},
_setMetaData: function(aNewContact, aRecord) {
aNewContact.id = aRecord.id;
aNewContact.published = aRecord.published;
aNewContact.updated = aRecord.updated;
},
_convertContact: function CM_convertContact(aContact) {
let newContact = new Contact();
newContact.init(aContact.properties);
this._setMetaData(newContact, aContact);
_convertContact: function(aContact) {
if (aContact.properties.bday) {
aContact.properties.bday = new Date(aContact.properties.bday);
}
if (aContact.properties.anniversary) {
aContact.properties.anniversary = new Date(aContact.properties.anniversary);
}
let newContact = new this._window.mozContact(aContact.properties);
newContact.setMetadata(aContact.id, aContact.published, aContact.updated);
return newContact;
},
@ -680,13 +512,11 @@ ContactManager.prototype = {
case "Contact:Changed":
// Fire oncontactchange event
if (DEBUG) debug("Contacts:ContactChanged: " + msg.contactID + ", " + msg.reason);
if (this._oncontactchange) {
let event = new this._window.MozContactChangeEvent("contactchanged", {
contactID: msg.contactID,
reason: msg.reason
});
this._oncontactchange.handleEvent(event);
}
let event = new this._window.MozContactChangeEvent("contactchange", {
contactID: msg.contactID,
reason: msg.reason
});
this.dispatchEvent(event);
break;
case "Contacts:Revision":
if (DEBUG) debug("new revision: " + msg.revision);
@ -708,6 +538,12 @@ ContactManager.prototype = {
this.removeRequest(msg.requestID);
},
dispatchEvent: function(event) {
if (this.hasListenPermission) {
this.__DOM_IMPL__.dispatchEvent(event);
}
},
askPermission: function (aAccess, aRequest, aAllowCallback, aCancelCallback) {
if (DEBUG) debug("askPermission for contacts");
let access;
@ -765,35 +601,53 @@ ContactManager.prototype = {
},
save: function save(aContact) {
if (DEBUG) debug("save: " + JSON.stringify(aContact) + " :" + aContact.id);
let newContact = {};
newContact.properties = {
name: [],
honorificPrefix: [],
givenName: [],
additionalName: [],
familyName: [],
honorificSuffix: [],
nickname: [],
email: [],
photo: [],
url: [],
category: [],
adr: [],
tel: [],
org: [],
jobTitle: [],
bday: null,
note: [],
impp: [],
anniversary: null,
sex: null,
genderIdentity: null,
key: [],
};
for (let field in newContact.properties) {
newContact.properties[field] = aContact[field];
// We have to do a deep copy of the contact manually here because
// nsFrameMessageManager doesn't know how to create a structured clone of a
// mozContact object.
let newContact = {properties: {}};
for (let field of PROPERTIES) {
if (aContact[field]) {
newContact.properties[field] = aContact[field];
}
}
for (let prop of ADDRESS_PROPERTIES) {
if (aContact[prop]) {
newContact.properties[prop] = [];
for (let i of aContact[prop]) {
if (i) {
let json = ContactAddress.prototype.toJSON.apply(i, [true]);
newContact.properties[prop].push(json);
}
}
}
}
for (let prop of FIELD_PROPERTIES) {
if (aContact[prop]) {
newContact.properties[prop] = [];
for (let i of aContact[prop]) {
if (i) {
let json = ContactField.prototype.toJSON.apply(i, [true]);
newContact.properties[prop].push(json);
}
}
}
}
for (let prop of TELFIELD_PROPERTIES) {
if (aContact[prop]) {
newContact.properties[prop] = [];
for (let i of aContact[prop]) {
if (i) {
let json = ContactTelField.prototype.toJSON.apply(i, [true]);
newContact.properties[prop].push(json);
}
}
}
}
let request = this.createRequest();
let requestID = this.getRequestId({request: request, reason: reason});
@ -801,7 +655,7 @@ ContactManager.prototype = {
if (aContact.id == "undefined") {
// for example {25c00f01-90e5-c545-b4d4-21E2ddbab9e0} becomes
// 25c00f0190e5c545b4d421E2ddbab9e0
aContact.id = this._getRandomId().replace('-', '', 'g').replace('{', '').replace('}', '');
aContact.id = this._getRandomId().replace(/[{}-]/g, "");
// Cache the contact so that its ID may be updated later if necessary
this._cachedContacts[requestID] = aContact;
reason = "create";
@ -809,8 +663,12 @@ ContactManager.prototype = {
reason = "update";
}
this._setMetaData(newContact, aContact);
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});
@ -886,19 +744,18 @@ ContactManager.prototype = {
let options = { id: aRecord.id };
let allowCallback = function() {
cpmm.sendAsyncMessage("Contact:Remove", {requestID: this.getRequestId({request: request, reason: "remove"}), options: options});
}.bind(this)
}.bind(this);
this.askPermission("remove", request, allowCallback);
return request;
},
clear: function() {
if (DEBUG) debug("clear");
let request;
request = this.createRequest();
let request = this.createRequest();
let options = {};
let allowCallback = function() {
cpmm.sendAsyncMessage("Contacts:Clear", {requestID: this.getRequestId({request: request, reason: "remove"}), options: options});
}.bind(this)
}.bind(this);
this.askPermission("remove", request, allowCallback);
return request;
},
@ -938,6 +795,7 @@ ContactManager.prototype = {
},
init: function(aWindow) {
// DOMRequestIpcHelper.initHelper sets this._window
this.initDOMRequestHelper(aWindow, ["Contacts:Find:Return:OK", "Contacts:Find:Return:KO",
"Contacts:Clear:Return:OK", "Contacts:Clear:Return:KO",
"Contact:Save:Return:OK", "Contact:Save:Return:KO",
@ -947,26 +805,22 @@ ContactManager.prototype = {
"Contacts:GetAll:Next", "Contacts:GetAll:Return:KO",
"Contacts:Count",
"Contacts:Revision", "Contacts:GetRevision:Return:KO",]);
let allowCallback = function() {
cpmm.sendAsyncMessage("Contacts:RegisterForMessages");
this.hasListenPermission = true;
}.bind(this);
this.askPermission("listen", null, allowCallback);
},
// Called from DOMRequestIpcHelper
uninit: function uninit() {
if (DEBUG) debug("uninit call");
if (this._oncontactchange)
this._oncontactchange = null;
},
classID: Components.ID("{8beb3a66-d70a-4111-b216-b8e995ad3aff}"),
contractID: "@mozilla.org/contactManager;1",
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
Ci.nsIDOMGlobalPropertyInitializer]),
};
classID : CONTACTMANAGER_CID,
QueryInterface : XPCOMUtils.generateQI([nsIDOMContactManager,
Ci.nsIDOMGlobalPropertyInitializer,
Ci.nsISupportsWeakReference]),
classInfo : XPCOMUtils.generateCI({classID: CONTACTMANAGER_CID,
contractID: CONTACTMANAGER_CONTRACTID,
classDescription: "ContactManager",
interfaces: [nsIDOMContactManager],
flags: nsIClassInfo.DOM_OBJECT})
}
this.NSGetFactory = XPCOMUtils.generateNSGetFactory(
[Contact, ContactManager, ContactProperties, ContactAddress, ContactField, ContactTelField, ContactFindSortOptions, ContactFindOptions])
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([
Contact, ContactManager, ContactAddress, ContactField, ContactTelField
]);

View File

@ -1,6 +1,3 @@
component {35ad8a4e-9486-44b6-883d-550f14635e49} ContactManager.js
contract @mozilla.org/contactProperties;1 {35ad8a4e-9486-44b6-883d-550f14635e49}
component {9cbfa81c-bcab-4ca9-b0d2-f4318f295e33} ContactManager.js
contract @mozilla.org/contactAddress;1 {9cbfa81c-bcab-4ca9-b0d2-f4318f295e33}
@ -10,16 +7,8 @@ contract @mozilla.org/contactField;1 {ad19a543-69e4-44f0-adfa-37c011556bc1}
component {4d42c5a9-ea5d-4102-80c3-40cc986367ca} ContactManager.js
contract @mozilla.org/contactTelField;1 {4d42c5a9-ea5d-4102-80c3-40cc986367ca}
component {0a5b1fab-70da-46dd-b902-619904d920c2} ContactManager.js
contract @mozilla.org/contactFindSortOptions;1 {0a5b1fab-70da-46dd-b902-619904d920c2}
component {28ce07d0-45d9-4b7a-8843-521df4edd8bc} ContactManager.js
contract @mozilla.org/contactFindOptions;1 {28ce07d0-45d9-4b7a-8843-521df4edd8bc}
component {72a5ee28-81d8-4af8-90b3-ae935396cc66} ContactManager.js
contract @mozilla.org/contact;1 {72a5ee28-81d8-4af8-90b3-ae935396cc66}
category JavaScript-global-constructor mozContact @mozilla.org/contact;1
component {8beb3a66-d70a-4111-b216-b8e995ad3aff} ContactManager.js
contract @mozilla.org/contactManager;1 {8beb3a66-d70a-4111-b216-b8e995ad3aff}
category JavaScript-navigator-property mozContacts @mozilla.org/contactManager;1

View File

@ -20,7 +20,7 @@ Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
Cu.import("resource://gre/modules/PhoneNumberUtils.jsm");
const DB_NAME = "contacts";
const DB_VERSION = 14;
const DB_VERSION = 15;
const STORE_NAME = "contacts";
const SAVED_GETALL_STORE_NAME = "getallcache";
const CHUNK_SIZE = 20;
@ -28,16 +28,10 @@ const REVISION_STORE = "revision";
const REVISION_KEY = "revision";
function exportContact(aRecord) {
let contact = {};
contact.properties = aRecord.properties;
for (let field in aRecord.properties)
contact.properties[field] = aRecord.properties[field];
contact.updated = aRecord.updated;
contact.published = aRecord.published;
contact.id = aRecord.id;
return contact;
if (aRecord) {
delete aRecord.search;
}
return aRecord;
}
function ContactDispatcher(aContacts, aFullContacts, aCallback, aNewTxn, aClearDispatcher, aFailureCb) {
@ -150,9 +144,7 @@ ContactDB.prototype = {
for (let i = 0; i < contacts.length; i++) {
let contact = {};
contact.properties = contacts[i];
contact.id = idService.generateUUID().toString().replace('-', '', 'g')
.replace('{', '')
.replace('}', '');
contact.id = idService.generateUUID().toString().replace(/[{}-]/g, "");
contact = this.makeImport(contact);
this.updateRecordMetadata(contact);
if (DEBUG) debug("import: " + JSON.stringify(contact));
@ -549,6 +541,47 @@ ContactDB.prototype = {
}
};
},
function upgrade14to15() {
if (DEBUG) debug("Fix array properties saved as scalars");
if (!objectStore) {
objectStore = aTransaction.objectStore(STORE_NAME);
}
const ARRAY_PROPERTIES = ["photo", "adr", "email", "url", "impp", "tel",
"name", "honorificPrefix", "givenName",
"additionalName", "familyName", "honorificSuffix",
"nickname", "category", "org", "jobTitle",
"note", "key"];
const PROPERTIES_WITH_TYPE = ["adr", "email", "url", "impp", "tel"];
objectStore.openCursor().onsuccess = function(event) {
let cursor = event.target.result;
let changed = false;
if (cursor) {
let props = cursor.value.properties;
for (let prop of ARRAY_PROPERTIES) {
if (props[prop]) {
if (!Array.isArray(props[prop])) {
cursor.value.properties[prop] = [props[prop]];
changed = true;
}
if (PROPERTIES_WITH_TYPE.indexOf(prop) !== -1) {
for (let subprop of cursor.value.properties[prop]) {
if (!Array.isArray(subprop.type)) {
subprop.type = [subprop.type];
changed = true;
}
}
}
}
}
if (changed) {
cursor.update(cursor.value);
}
cursor.continue();
} else {
next();
}
};
},
];
let index = aOldVersion;
@ -576,31 +609,7 @@ ContactDB.prototype = {
},
makeImport: function makeImport(aContact) {
let contact = {};
contact.properties = {
name: [],
honorificPrefix: [],
givenName: [],
additionalName: [],
familyName: [],
honorificSuffix: [],
nickname: [],
email: [],
photo: [],
url: [],
category: [],
adr: [],
tel: [],
org: [],
jobTitle: [],
bday: null,
note: [],
impp: [],
anniversary: null,
sex: null,
genderIdentity: null,
key: [],
};
let contact = {properties: {}};
contact.search = {
givenName: [],
@ -687,7 +696,6 @@ ContactDB.prototype = {
}
}
}
if (DEBUG) debug("contact:" + JSON.stringify(contact));
contact.updated = aContact.updated;
contact.published = aContact.published;

View File

@ -61,29 +61,30 @@ var c4 = {
};
var c5 = {
nickname: "empty"
familyName: [],
givenName: [],
};
var c6 = {
name: "e",
name: ["e"],
familyName: ["e","e","e"],
givenName: ["e","e","e"],
};
var c7 = {
name: "e",
name: ["e"],
familyName: ["e","e","e"],
givenName: ["e","e","e"],
};
var c8 = {
name: "e",
name: ["e"],
familyName: ["e","e","e"],
givenName: ["e","e","e"],
};
var adr1 = {
type: "work",
type: ["work"],
streetAddress: "street 1",
locality: "locality 1",
region: "region 1",
@ -92,7 +93,7 @@ var adr1 = {
};
var adr2 = {
type: "home, fax",
type: ["home, fax"],
streetAddress: "street2",
locality: "locality2",
region: "region2",
@ -104,34 +105,34 @@ var properties1 = {
name: ["Test1 TestFamilyName", "Test2 Wagner"],
familyName: ["TestFamilyName","Wagner"],
givenName: ["Test1","Test2"],
nickname: "nicktest",
nickname: ["nicktest"],
tel: [{type: ["work"], value: "123456", carrier: "testCarrier"} , {type: ["home", "fax"], value: "+55 (31) 9876-3456"}, {type: ["home"], value: "+49 451 491934"}],
adr: adr1,
adr: [adr1],
email: [{type: ["work"], value: "x@y.com"}],
};
var properties2 = {
name: ["dummyHonorificPrefix dummyGivenName dummyFamilyName dummyHonorificSuffix", "dummyHonorificPrefix2"],
familyName: "dummyFamilyName",
givenName: "dummyGivenName",
familyName: ["dummyFamilyName"],
givenName: ["dummyGivenName"],
honorificPrefix: ["dummyHonorificPrefix","dummyHonorificPrefix2"],
honorificSuffix: "dummyHonorificSuffix",
additionalName: "dummyadditionalName",
nickname: "dummyNickname",
honorificSuffix: ["dummyHonorificSuffix"],
additionalName: ["dummyadditionalName"],
nickname: ["dummyNickname"],
tel: [{type: ["test"], value: "7932012345", carrier: "myCarrier", pref: 1},{type: ["home", "custom"], value: "7932012346", pref: 0}],
email: [{type: ["test"], value: "a@b.c"}, {value: "b@c.d", pref: 1}],
adr: [adr1, adr2],
impp: [{type: ["aim"], value:"im1", pref: 1}, {value: "im2"}],
org: ["org1", "org2"],
jobTitle: ["boss", "superboss"],
note: "test note",
note: ["test note"],
category: ["cat1", "cat2"],
url: [{type: ["work", "work2"], value: "www.1.com", pref: 1}, {value:"www2.com"}],
bday: new Date("1980, 12, 01"),
anniversary: new Date("2000, 12, 01"),
sex: "male",
genderIdentity: "test",
key: "ERPJ394GJJWEVJ0349GJ09W3H4FG0WFW80VHW3408GH30WGH348G3H"
key: ["ERPJ394GJJWEVJ0349GJ09W3H4FG0WFW80VHW3408GH30WGH348G3H"]
};
var sample_id1;
@ -166,20 +167,39 @@ function checkStr(str1, str2, msg) {
ok(false, "Expected both strings to be either present or absent");
return;
}
if (!str1 || str1 == "null") {
str1 = null;
}
if (!str2 || str2 == "null") {
str2 = null;
}
is(str1, str2, msg);
}
function checkStrArray(str1, str2, msg) {
// comparing /[null(,null)+]/ and undefined should pass
function nonNull(e) {
return e != null;
function normalize_falsy(v) {
if (!v || v == "null" || v == "undefined") {
return "";
}
return v;
}
if ((Array.isArray(str1) && str1.filter(nonNull).length == 0 && str2 == undefined)
||(Array.isArray(str2) && str2.filter(nonNull).length == 0 && str1 == undefined)) {
ok(true, msg);
} else if (str1) {
is(JSON.stringify(typeof str1 == "string" ? [str1] : str1), JSON.stringify(typeof str2 == "string" ? [str2] : str2), msg);
function optArray(val) {
return Array.isArray(val) ? val : [val];
}
str1 = optArray(str1).map(normalize_falsy).filter(v => v != "");
str2 = optArray(str2).map(normalize_falsy).filter(v => v != "");
ise(JSON.stringify(str1), JSON.stringify(str2), msg);
}
function checkPref(pref1, pref2) {
// If on Android treat one preference as 0 and the other as undefined as matching
if (isAndroid) {
if ((!pref1 && pref2 == undefined) || (pref1 == undefined && !pref2)) {
pref1 = false;
pref2 = false;
}
}
ise(!!pref1, !!pref2, "Same pref");
}
function checkAddress(adr1, adr2) {
@ -188,44 +208,31 @@ function checkAddress(adr1, adr2) {
return;
}
checkStrArray(adr1.type, adr2.type, "Same type");
checkStrArray(adr1.streetAddress, adr2.streetAddress, "Same streetAddress");
checkStrArray(adr1.locality, adr2.locality, "Same locality");
checkStrArray(adr1.region, adr2.region, "Same region");
checkStrArray(adr1.postalCode, adr2.postalCode, "Same postalCode");
checkStrArray(adr1.countryName, adr2.countryName, "Same countryName");
checkStr(adr1.streetAddress, adr2.streetAddress, "Same streetAddress");
checkStr(adr1.locality, adr2.locality, "Same locality");
checkStr(adr1.region, adr2.region, "Same region");
checkStr(adr1.postalCode, adr2.postalCode, "Same postalCode");
checkStr(adr1.countryName, adr2.countryName, "Same countryName");
checkPref(adr1.pref, adr2.pref);
}
function checkTel(tel1, tel2) {
if (tel1 ^ tel2) {
ok(false, "Expected both tels to be either present or absent");
return;
}
checkStrArray(tel1.type, tel2.type, "Same type");
checkStrArray(tel1.value, tel2.value, "Same value");
checkStrArray(tel1.carrier, tel2.carrier, "Same carrier");
checkPref(tel1.pref, tel2.pref);
}
function checkField(field1, field2) {
if (field1 ^ field2) {
ok(false, "Expected both fields to be either present or absent");
return;
}
checkStrArray(field1.type, field2.type, "Same type");
checkStrArray(field1.value, field2.value, "Same value");
checkStr(field1.value, field2.value, "Same value");
checkPref(field1.pref, field2.pref);
}
function checkPref(pref1, pref2) {
// If on Android treat one preference as 0 and the other as undefined as matching
if (isAndroid) {
if ((pref1 == 0 && pref2 == undefined) || (pref1 == undefined && pref2 == 0)) {
pref1 = 0;
pref2 = 0;
}
function checkTel(tel1, tel2) {
if (tel1 ^ tel2) {
ok(false, "Expected both tels to be either present or absent");
return;
}
is(pref1, pref2, "Same pref");
checkField(tel1, tel2);
checkStr(tel1.carrier, tel2.carrier, "Same carrier");
}
function checkCategory(category1, category2) {
@ -239,21 +246,42 @@ function checkCategory(category1, category2) {
}
function removeAndroidDefaultCategory(category) {
if (category == undefined) {
return;
if (!category) {
return category;
}
for (var i = 0; i < category.length; i++) {
var result = [];
for (var i of category) {
// Some devices may return the full group name (prefixed with "System Group: ")
if (category[i] == "My Contacts" || category[i] == "System Group: My Contacts") {
category.splice(i, 1);
if (i != "My Contacts" && i != "System Group: My Contacts") {
result.push(i);
}
}
return category;
return result;
}
function checkArrayField(array1, array2, func, msg) {
if (!!array1 ^ !!array2) {
ok(false, "Expected both arrays to be either present or absent");
return;
}
if (!array1 && !array2) {
ok(true, msg);
return;
}
ise(array1.length, array2.length, "Same length");
for (var i = 0; i < array1.length; ++i) {
func(array1[i], array2[i], msg);
}
}
function checkContacts(contact1, contact2) {
if (!!contact1 ^ !!contact2) {
ok(false, "Expected both contacts to be either present or absent");
return;
}
checkStrArray(contact1.name, contact2.name, "Same name");
checkStrArray(contact1.honorificPrefix, contact2.honorificPrefix, "Same honorificPrefix");
checkStrArray(contact1.givenName, contact2.givenName, "Same givenName");
@ -271,21 +299,11 @@ function checkContacts(contact1, contact2) {
checkStr(contact1.genderIdentity, contact2.genderIdentity, "Same genderIdentity");
checkStrArray(contact1.key, contact2.key, "Same key");
for (var i in contact1.email) {
checkField(contact1.email[i], contact2.email[i]);
}
for (var i in contact1.adr) {
checkAddress(contact1.adr[i], contact2.adr[i]);
}
for (var i in contact1.tel) {
checkTel(contact1.tel[i], contact2.tel[i]);
}
for (var i in contact1.url) {
checkField(contact1.url[i], contact2.url[i]);
}
for (var i in contact1.impp) {
checkField(contact1.impp[i], contact2.impp[i]);
}
checkArrayField(contact1.adr, contact2.adr, checkAddress, "Same adr");
checkArrayField(contact1.tel, contact2.tel, checkTel, "Same tel");
checkArrayField(contact1.email, contact2.email, checkField, "Same email");
checkArrayField(contact1.url, contact2.url, checkField, "Same url");
checkArrayField(contact1.impp, contact2.impp, checkField, "Same impp");
}
var req;
@ -293,6 +311,10 @@ var index = 0;
var initialRev;
var defaultOptions = {
sortBy: "givenName",
};
function checkRevision(revision, msg, then) {
var revReq = mozContacts.getRevision();
revReq.onsuccess = function(e) {
@ -319,8 +341,6 @@ function checkCount(count, msg, then) {
}
var mozContacts = window.navigator.mozContacts;
ok(mozContacts, "mozContacts exists");
ok("mozContact" in window, "mozContact exists");
var steps = [
function() {
req = mozContacts.getRevision();
@ -354,7 +374,7 @@ var steps = [
},
function () {
ok(true, "Retrieving all contacts");
req = mozContacts.find({});
req = mozContacts.find(defaultOptions);
req.onsuccess = function () {
is(req.result.length, 0, "Empty database.");
checkRevision(1, "Revision was not incremented on find", next);
@ -363,8 +383,7 @@ var steps = [
},
function () {
ok(true, "Adding empty contact");
createResult1 = new mozContact();
createResult1.init({});
createResult1 = new mozContact({});
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
@ -377,7 +396,7 @@ var steps = [
},
function () {
ok(true, "Retrieving all contacts");
req = mozContacts.find({});
req = mozContacts.find(defaultOptions);
req.onsuccess = function () {
is(req.result.length, 1, "One contact.");
findResult1 = req.result[0];
@ -389,7 +408,7 @@ var steps = [
ok(true, "Deleting empty contact");
req = navigator.mozContacts.remove(findResult1);
req.onsuccess = function () {
var req2 = mozContacts.find({});
var req2 = mozContacts.find(defaultOptions);
req2.onsuccess = function () {
is(req2.result.length, 0, "Empty Database.");
clearTemps();
@ -401,8 +420,7 @@ var steps = [
},
function () {
ok(true, "Adding a new contact1");
createResult1 = new mozContact();
createResult1.init(properties1);
createResult1 = new mozContact(properties1);
mozContacts.oncontactchange = function(event) {
is(event.contactID, createResult1.id, "Same contactID");
@ -487,8 +505,7 @@ var steps = [
is(event.contactID, createResult2.id, "Same contactID");
is(event.reason, "create", "Same reason");
}
createResult2 = new mozContact();
createResult2.init({name: "newName"});
createResult2 = new mozContact({name: ["newName"]});
req = navigator.mozContacts.save(createResult2);
req.onsuccess = function () {
ok(createResult2.id, "The contact now has an ID.");
@ -573,8 +590,7 @@ var steps = [
},
function () {
ok(true, "Adding a new contact with properties1");
createResult1 = new mozContact();
createResult1.init(properties1);
createResult1 = new mozContact(properties1);
mozContacts.oncontactchange = null;
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
@ -788,14 +804,16 @@ var steps = [
},
function () {
ok(true, "Retrieving all contacts");
req = mozContacts.find({});
req = mozContacts.find(defaultOptions);
req.onsuccess = function() {
is(req.result.length, 1, "Found exactly 1 contact.");
findResult1 = req.result[0];
ok(findResult1.id == sample_id1, "Same ID");
checkContacts(createResult1, findResult1);
ok(findResult1.updated, "Has updated field");
ok(findResult1.published, "Has published field");
if (!isAndroid) {
ok(findResult1.updated, "Has updated field");
ok(findResult1.published, "Has published field");
}
next();
}
req.onerror = onFailure;
@ -808,7 +826,7 @@ var steps = [
findResult1.impp = properties1.impp = [{value:"phil impp"}];
req = navigator.mozContacts.save(findResult1);
req.onsuccess = function () {
var req2 = mozContacts.find({});
var req2 = mozContacts.find(defaultOptions);
req2.onsuccess = function() {
is(req2.result.length, 1, "Found exactly 1 contact.");
findResult2 = req2.result[0];
@ -872,7 +890,7 @@ var steps = [
findResult1.impp = properties1.impp = [{value: "phil impp"}];
req = mozContacts.save(findResult1);
req.onsuccess = function () {
var req2 = mozContacts.find({});
var req2 = mozContacts.find(defaultOptions);
req2.onsuccess = function () {
is(req2.result.length, 1, "Found exactly 1 contact.");
findResult1 = req2.result[0];
@ -937,9 +955,8 @@ var steps = [
SpecialPowers.executeSoon(next);
} else {
findResult1.email = [{value: properties1.nickname}];
findResult1.nickname = "TEST";
var newContact = new mozContact();
newContact.init(findResult1);
findResult1.nickname = ["TEST"];
var newContact = new mozContact(findResult1);
req = mozContacts.save(newContact);
req.onsuccess = function () {
var options = {filterBy: ["email", "givenName"],
@ -961,7 +978,7 @@ var steps = [
ok(true, "Deleting contact" + findResult1);
req = mozContacts.remove(findResult1);
req.onsuccess = function () {
var req2 = mozContacts.find({});
var req2 = mozContacts.find(defaultOptions);
req2.onsuccess = function () {
is(req2.result.length, 1, "One contact left.");
findResult1 = req2.result[0];
@ -982,8 +999,7 @@ var steps = [
},
function () {
ok(true, "Adding a new contact");
createResult1 = new mozContact();
createResult1.init(properties1);
createResult1 = new mozContact(properties1);
req = mozContacts.save(createResult1)
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
@ -994,8 +1010,7 @@ var steps = [
},
function () {
ok(true, "Adding a new contact2");
createResult2 = new mozContact();
createResult2.init(properties2);
createResult2 = new mozContact(properties2);
req = mozContacts.save(createResult2);
req.onsuccess = function () {
ok(createResult2.id, "The contact now has an ID.");
@ -1006,10 +1021,10 @@ var steps = [
},
function () {
ok(true, "Retrieving all contacts");
req = mozContacts.find({sortBy: 'FamilyName',})
req = mozContacts.find({sortBy: "familyName"});
req.onsuccess = function () {
is(req.result.length, 2, "Found exactly 2 contact.");
checkContacts(properties2, req.result[1]);
checkContacts(req.result[1], properties1);
next();
}
req.onerror = onFailure;
@ -1086,16 +1101,14 @@ var steps = [
function () {
ok(true, "Adding 20 contacts");
for (var i=0; i<19; i++) {
createResult1 = new mozContact();
createResult1.init(properties1);
createResult1 = new mozContact(properties1);
req = mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
};
req.onerror = onFailure;
};
createResult1 = new mozContact();
createResult1.init(properties1);
createResult1 = new mozContact(properties1);
req = mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
@ -1106,7 +1119,7 @@ var steps = [
},
function () {
ok(true, "Retrieving all contacts");
req = mozContacts.find({});
req = mozContacts.find(defaultOptions);
req.onsuccess = function () {
is(req.result.length, 20, "20 Entries.");
next();
@ -1152,11 +1165,10 @@ var steps = [
ok(true, "Retrieving all contacts3");
var options = {filterBy: ["givenName", "tel", "email"],
filterOp: "startsWith",
filterValue: properties1.givenName[0].substring(0, 4),
filterLimit: 15 };
filterValue: properties1.givenName[0].substring(0, 4)};
req = mozContacts.find(options);
req.onsuccess = function () {
is(req.result.length, 15, "15 Entries.");
is(req.result.length, 20, "20 Entries.");
checkContacts(createResult1, req.result[10]);
next();
}
@ -1173,8 +1185,7 @@ var steps = [
},
function () {
ok(true, "Testing clone contact");
createResult1 = new mozContact();
createResult1.init(properties1);
createResult1 = new mozContact(properties1);
req = mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
@ -1187,14 +1198,14 @@ var steps = [
ok(true, "Testing clone contact2");
var cloned = new mozContact(createResult1);
ok(cloned.id != createResult1.id, "Cloned contact has new ID");
cloned.email = {value: "new email!"};
cloned.givenName = "Tom";
cloned.email = [{value: "new email!"}];
cloned.givenName = ["Tom"];
req = mozContacts.save(cloned);
req.onsuccess = function () {
ok(cloned.id, "The contact now has an ID.");
ok(cloned.email[0].value == "new email!", "Same Email");
ok(createResult1.email != cloned.email, "Clone has different email");
ok(cloned.givenName == "Tom", "New Name");
is(cloned.email[0].value, "new email!", "Same Email");
isnot(createResult1.email[0].value, cloned.email[0].value, "Clone has different email");
is(cloned.givenName, "Tom", "New Name");
next();
}
req.onerror = onFailure;
@ -1204,7 +1215,7 @@ var steps = [
var options = {filterBy: ["givenName"],
filterOp: "startsWith",
filterValue: properties2.givenName[0].substring(0, 4)};
req = mozContacts.find({});
req = mozContacts.find(defaultOptions);
req.onsuccess = function () {
is(req.result.length, 2, "2 Entries.");
next();
@ -1213,11 +1224,14 @@ var steps = [
},
function () {
ok(true, "Search with redundant fields should only return 1 contact");
createResult1 = new mozContact();
createResult1.init({name: "XXX", givenName: "XXX", email: [{value: "XXX"}], tel: {value: "XXX"}});
createResult1 = new mozContact({name: ["XXX"],
givenName: ["XXX"],
email: [{value: "XXX"}],
tel: [{value: "XXX"}]
});
req = mozContacts.save(createResult1);
req.onsuccess = function() {
var options = {filterBy: [],
var options = {filterBy: ["givenName", "familyName"],
filterOp: "equals",
filterValue: "XXX"};
var req2 = mozContacts.find(options);
@ -1240,8 +1254,7 @@ var steps = [
},
function () {
ok(true, "Test sorting");
createResult1 = new mozContact();
createResult1.init(c3);
createResult1 = new mozContact(c3);
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
@ -1252,8 +1265,7 @@ var steps = [
},
function () {
ok(true, "Test sorting");
createResult1 = new mozContact();
createResult1.init(c2);
createResult1 = new mozContact(c2);
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
@ -1264,8 +1276,7 @@ var steps = [
},
function () {
ok(true, "Test sorting");
createResult1 = new mozContact();
createResult1.init(c4);
createResult1 = new mozContact(c4);
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
@ -1276,8 +1287,7 @@ var steps = [
},
function () {
ok(true, "Test sorting");
createResult1 = new mozContact();
createResult1.init(c1);
createResult1 = new mozContact(c1);
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
@ -1318,8 +1328,7 @@ var steps = [
},
function () {
ok(true, "Test sorting");
createResult1 = new mozContact();
createResult1.init(c5);
createResult1 = new mozContact(c5);
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
@ -1346,17 +1355,16 @@ var steps = [
},
function () {
ok(true, "Don't allow to add custom fields");
createResult1 = new mozContact();
createResult1.init({givenName: "customTest", yyy: "XXX"});
createResult1 = new mozContact({givenName: ["customTest"], yyy: "XXX"});
req = mozContacts.save(createResult1);
req.onsuccess = function() {
var options = {filterBy: [],
var options = {filterBy: ["givenName"],
filterOp: "equals",
filterValue: "customTest"};
var req2 = mozContacts.find(options);
req2.onsuccess = function() {
is(req2.result.length, 1, "1 Entry");
checkStrArray(req2.result.givenName, "customTest", "same name");
checkStrArray(req2.result[0].givenName, ["customTest"], "same name");
ok(req2.result.yyy === undefined, "custom property undefined");
next();
}
@ -1375,8 +1383,7 @@ var steps = [
},
function () {
ok(true, "Test sorting");
createResult1 = new mozContact();
createResult1.init(c7);
createResult1 = new mozContact(c7);
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
@ -1387,8 +1394,7 @@ var steps = [
},
function () {
ok(true, "Test sorting");
createResult1 = new mozContact();
createResult1.init(c6);
createResult1 = new mozContact(c6);
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
@ -1399,8 +1405,7 @@ var steps = [
},
function () {
ok(true, "Test sorting");
createResult1 = new mozContact();
createResult1.init(c8);
createResult1 = new mozContact(c8);
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
@ -1430,7 +1435,7 @@ var steps = [
},
function () {
ok(true, "Deleting database");
req = mozContacts.clear()
req = mozContacts.clear();
req.onsuccess = function () {
ok(true, "Deleted the database");
next();
@ -1439,8 +1444,7 @@ var steps = [
},
function () {
ok(true, "Adding a new contact with properties2");
createResult2 = new mozContact();
createResult2.init(properties2);
createResult2 = new mozContact(properties2);
req = mozContacts.save(createResult2);
req.onsuccess = function () {
ok(createResult2.id, "The contact now has an ID.");
@ -1485,9 +1489,8 @@ var steps = [
req.onerror = onFailure;
},
function () {
ok(true, "Adding empty contact");
createResult1 = new mozContact();
createResult1.init({name: "5", givenName: "5"});
ok(true, "Adding contact for category search");
createResult1 = new mozContact({name: ["5"], givenName: ["5"]});
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
@ -1522,20 +1525,10 @@ var steps = [
ok(true, "Adding contact with invalid data");
var input = document.createElement("input");
var obj = {
name: [1, 2],
familyName: 3,
givenName: 4,
honorificPrefix: [],
honorificSuffix: {foo: "bar"},
additionalName: 7,
nickname: [8, 9],
org: [10, 11],
jobTitle: [12, 13],
note: 14,
category: [15, 16],
honorificSuffix: [{foo: "bar"}],
sex: 17,
genderIdentity: 18,
key: 4,
email: input,
adr: input,
tel: input,
@ -1553,12 +1546,12 @@ var steps = [
}
}
})());
createResult1 = new mozContact();
createResult1.init(obj);
createResult1 = new mozContact(obj);
req = mozContacts.save(createResult1);
req.onsuccess = function () {
checkContacts(createResult1, {
honorificPrefix: "string",
honorificPrefix: ["string"],
honorificSuffix: ["[object Object]"],
sex: "17",
genderIdentity: "18"
});
@ -1567,8 +1560,7 @@ var steps = [
},
function () {
ok(true, "Adding contact with no number but carrier");
createResult1 = new mozContact();
createResult1.init({ tel: [{type: ["home"], carrier: "myCarrier"} ] });
createResult1 = new mozContact({ tel: [{type: ["home"], carrier: "myCarrier"} ] });
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
@ -1578,8 +1570,7 @@ var steps = [
},
function () {
ok(true, "Adding contact with email but no value");
createResult1 = new mozContact();
createResult1.init({ email: [{type: ["home"]}] });
createResult1 = new mozContact({ email: [{type: ["home"]}] });
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
@ -1589,8 +1580,7 @@ var steps = [
},
function () {
ok(true, "Testing numbersOnly search 1");
createResult1 = new mozContact();
createResult1.init({ name: ["aaaaaaaaa"], givenName: ["aaaaaaaaa"], tel: [{ value: "1234567890"}]});
createResult1 = new mozContact({ name: ["aaaaaaaaa"], givenName: ["aaaaaaaaa"], tel: [{ value: "1234567890"}]});
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
@ -1670,17 +1660,10 @@ var steps = [
},
function() {
ok(true, "Test setting array properties to scalar values")
const DOMStrings = ["name","honorificPrefix","givenName","additionalName",
"familyName", "honorificSuffix","nickname","category",
"org","jobTitle","note"];
const FIELDS = ["email","url","adr","tel","impp"];
createResult1 = new mozContact();
for (var prop of DOMStrings) {
createResult1[prop] = "foo";
ok(Array.isArray(createResult1[prop]), prop + " is array");
}
for (var prop of FIELDS) {
createResult1[prop] = {type: "foo"};
createResult1[prop] = {type: ["foo"]};
ok(Array.isArray(createResult1[prop]), prop + " is array");
}
next();

View File

@ -90,14 +90,14 @@ var randomBlob = getRandomBlob(1024);
var randomBlob2 = getRandomBlob(1024);
var properties1 = {
name: "xTestname1",
givenName: "xTestname1",
name: ["xTestname1"],
givenName: ["xTestname1"],
photo: [randomBlob]
};
var properties2 = {
name: "yTestname2",
givenName: "yTestname2",
name: ["yTestname2"],
givenName: ["yTestname2"],
photo: [randomBlob, randomBlob2]
};
@ -177,8 +177,7 @@ var steps = [
},
function () {
ok(true, "Adding contact with photo");
createResult1 = new mozContact();
createResult1.init(properties1);
createResult1 = new mozContact(properties1);
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
@ -191,7 +190,7 @@ var steps = [
ok(true, "Retrieving by substring");
var options = {filterBy: ["givenName"],
filterOp: "startsWith",
filterValue: properties1.givenName.substring(0,3)};
filterValue: properties1.givenName[0].substring(0,3)};
req = mozContacts.find(options);
req.onsuccess = function () {
ok(req.result.length == 1, "Found exactly 1 contact.");
@ -203,8 +202,7 @@ var steps = [
},
function () {
ok(true, "Adding contact with 2 photos");
createResult1 = new mozContact();
createResult1.init(properties2);
createResult1 = new mozContact(properties2);
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
@ -217,7 +215,7 @@ var steps = [
ok(true, "Retrieving by substring");
var options = {filterBy: ["givenName"],
filterOp: "startsWith",
filterValue: properties2.givenName.substring(0,3)};
filterValue: properties2.givenName[0].substring(0,3)};
req = mozContacts.find(options);
req.onsuccess = function () {
ok(req.result.length == 1, "Found exactly 1 contact.");
@ -227,58 +225,6 @@ var steps = [
};
req.onerror = onFailure;
},
function () {
ok(true, "Adding photo as String");
createResult1 = new mozContact();
createResult1.init({givenName: "asdf", photo: ["xyz"]});
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
sample_id1 = createResult1.id;
is(createResult1.photo, null, "No photo")
next();
};
req.onerror = onFailure;
},
function () {
ok(true, "Adding photo as String");
createResult1 = new mozContact();
createResult1.init({givenName: "jkl", photo: "xyz"});
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
is(createResult1.photo, null, "No photo")
next();
};
req.onerror = onFailure;
},
function () {
ok(true, "Retrieving by substring");
var options = {filterBy: ["givenName"],
filterOp: "startsWith",
filterValue: "asdf"};
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");
is(findResult1.photo, null, "No photo");
next();
};
req.onerror = onFailure;
},
function () {
ok(true, "Adding photo as Object");
createResult1 = new mozContact();
createResult1.init({photo: [{}]});
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
is(createResult1.photo, null, "No photo")
next();
};
req.onerror = onFailure;
},
function () {
ok(true, "Deleting database");
req = mozContacts.clear()

View File

@ -36,7 +36,7 @@ var androidVersion = SpecialPowers.Cc['@mozilla.org/system-info;1']
.getProperty('version');
let adr1 = {
type: "work",
type: ["work"],
streetAddress: "street 1",
locality: "locality 1",
region: "region 1",
@ -48,9 +48,9 @@ let properties1 = {
name: ["Testname1 TestFamilyName"],
familyName: ["TestFamilyName","Wagner"],
givenName: ["Test1","Test2"],
nickname: "nicktest",
nickname: ["nicktest"],
tel: [{type: ["work"], value: "123456", carrier: "testCarrier"} , {type: ["home", "fax"], value: "+9-876-5432"}],
adr: adr1,
adr: [adr1],
email: [{type: ["work"], value: "x@y.com"}]
};
@ -58,102 +58,137 @@ function onFailure() {
ok(false, "in on Failure!");
next();
}
function checkStr(str1, str2, msg) {
// comparing /[null(,null)+]/ and undefined should pass
function nonNull(e) {
return e != null;
if (str1 ^ str2) {
ok(false, "Expected both strings to be either present or absent");
return;
}
if ((Array.isArray(str1) && str1.filter(nonNull).length == 0 && str2 == undefined)
||(Array.isArray(str2) && str2.filter(nonNull).length == 0 && str1 == undefined)) {
ok(true, msg);
} else if (str1) {
is(JSON.stringify(typeof str1 == "string" ? [str1] : str1), JSON.stringify(typeof str2 == "string" ? [str2] : str2), msg);
is(str1, str2, msg);
}
function checkStrArray(str1, str2, msg) {
function normalize_falsy(k, v) {
if (!v || v == "null" || v == "undefined") {
return "";
}
return v;
}
ise(JSON.stringify(str1, normalize_falsy), JSON.stringify(str2, normalize_falsy), msg);
}
function checkPref(pref1, pref2) {
// If on Android treat one preference as 0 and the other as undefined as matching
if (isAndroid) {
if ((!pref1 && pref2 == undefined) || (pref1 == undefined && !pref2)) {
pref1 = false;
pref2 = false;
}
}
ise(!!pref1, !!pref2, "Same pref");
}
function checkAddress(adr1, adr2) {
checkStr(adr1.type, adr2.type, "Same type");
checkStr(adr1.streetAddress, adr2.streetAddress, "Same streetAddress");
checkStr(adr1.locality, adr2.locality, "Same locality");
checkStr(adr1.region, adr2.region, "Same region");
checkStr(adr1.postalCode, adr2.postalCode, "Same postalCode");
checkStr(adr1.countryName, adr2.countryName, "Same countryName");
}
function checkTel(tel1, tel2) {
checkStr(tel1.type, tel2.type, "Same type");
checkStr(tel1.value, tel2.value, "Same value");
checkStr(tel1.carrier, tel2.carrier, "Same carrier");
if (adr1 ^ adr2) {
ok(false, "Expected both adrs to be either present or absent");
return;
}
checkStrArray(adr1.type, adr2.type, "Same type");
checkStrArray(adr1.streetAddress, adr2.streetAddress, "Same streetAddress");
checkStrArray(adr1.locality, adr2.locality, "Same locality");
checkStrArray(adr1.region, adr2.region, "Same region");
checkStrArray(adr1.postalCode, adr2.postalCode, "Same postalCode");
checkStrArray(adr1.countryName, adr2.countryName, "Same countryName");
checkPref(adr1.pref, adr2.pref);
}
function checkField(field1, field2) {
checkStr(field1.type, field2.type, "Same type");
checkStr(field1.value, field2.value, "Same value");
if (field1 ^ field2) {
ok(false, "Expected both fields to be either present or absent");
return;
}
checkStrArray(field1.type, field2.type, "Same type");
checkStrArray(field1.value, field2.value, "Same value");
checkPref(field1.pref, field2.pref);
}
function checkTel(tel1, tel2) {
if (tel1 ^ tel2) {
ok(false, "Expected both tels to be either present or absent");
return;
}
checkField(tel1, tel2);
checkStrArray(tel1.carrier, tel2.carrier, "Same carrier");
}
function checkCategory(category1, category2) {
// Android adds contacts to the a default category. This should be removed from the
// results before comparing them
if (isAndroid) {
category1 = removeAndroidDefaultCategory(category1);
category2 = removeAndroidDefaultCategory(category2);
}
checkStrArray(category1, category2, "Same Category")
}
function removeAndroidDefaultCategory(category) {
if (!category) {
return category;
}
var result = [];
for (var i of category) {
// Some devices may return the full group name (prefixed with "System Group: ")
if (i != "My Contacts" && i != "System Group: My Contacts") {
result.push(i);
}
}
return result;
}
function checkArrayField(array1, array2, func, msg) {
if (!!array1 ^ !!array2) {
ok(false, "Expected both arrays to be either present or absent");
return;
}
if (!array1 && !array2) {
ok(true, msg);
return;
}
ise(array1.length, array2.length, "Same length");
for (var i = 0; i < array1.length; ++i) {
func(array1[i], array2[i], msg);
}
}
function checkContacts(contact1, contact2) {
checkStr(contact1.name, contact2.name, "Same name");
checkStr(contact1.honorificPrefix, contact2.honorificPrefix, "Same honorificPrefix");
checkStr(contact1.givenName, contact2.givenName, "Same givenName");
checkStr(contact1.additionalName, contact2.additionalName, "Same additionalName");
checkStr(contact1.familyName, contact2.familyName, "Same familyName");
checkStr(contact1.honorificSuffix, contact2.honorificSuffix, "Same honorificSuffix");
checkStr(contact1.nickname, contact2.nickname, "Same nickname");
checkStr(contact1.category, contact2.category, "Same category");
checkStr(contact1.org, contact2.org, "Same org");
checkStr(contact1.jobTitle, contact2.jobTitle, "Same jobTitle");
if (!!contact1 ^ !!contact2) {
ok(false, "Expected both contacts to be either present or absent");
return;
}
checkStrArray(contact1.name, contact2.name, "Same name");
checkStrArray(contact1.honorificPrefix, contact2.honorificPrefix, "Same honorificPrefix");
checkStrArray(contact1.givenName, contact2.givenName, "Same givenName");
checkStrArray(contact1.additionalName, contact2.additionalName, "Same additionalName");
checkStrArray(contact1.familyName, contact2.familyName, "Same familyName");
checkStrArray(contact1.honorificSuffix, contact2.honorificSuffix, "Same honorificSuffix");
checkStrArray(contact1.nickname, contact2.nickname, "Same nickname");
checkCategory(contact1.category, contact2.category);
checkStrArray(contact1.org, contact2.org, "Same org");
checkStrArray(contact1.jobTitle, contact2.jobTitle, "Same jobTitle");
is(contact1.bday ? contact1.bday.valueOf() : null, contact2.bday ? contact2.bday.valueOf() : null, "Same birthday");
checkStr(contact1.note, contact2.note, "Same note");
checkStrArray(contact1.note, contact2.note, "Same note");
is(contact1.anniversary ? contact1.anniversary.valueOf() : null , contact2.anniversary ? contact2.anniversary.valueOf() : null, "Same anniversary");
is(contact1.sex, contact2.sex, "Same sex");
is(contact1.genderIdentity, contact2.genderIdentity, "Same genderIdentity");
checkStr(contact1.sex, contact2.sex, "Same sex");
checkStr(contact1.genderIdentity, contact2.genderIdentity, "Same genderIdentity");
checkStrArray(contact1.key, contact2.key, "Same key");
for (let i in contact1.email) {
if (contact1.email) {
ok(contact2.email != null, "conatct2.email exists");
}
if (contact2.email) {
ok(contact1.email != null, "conatct1.email exists");
}
checkField(contact1.email[i], contact2.email[i]);
}
for (let i in contact1.adr) {
if (contact1.adr) {
ok(contact2.adr != null, "conatct2.adr exists");
}
if (contact2.adr) {
ok(contact1.adr != null, "conatct1.adr exists");
}
checkAddress(contact1.adr[i], contact2.adr[i]);
}
for (let i in contact1.tel) {
if (contact1.tel) {
ok(contact2.tel != null, "conatct2.tel exists");
}
if (contact2.tel) {
ok(contact1.tel != null, "conatct1.tel exists");
}
checkTel(contact1.tel[i], contact2.tel[i]);
}
for (let i in contact1.url) {
if (contact1.url) {
ok(contact2.url != null, "conatct2.url exists");
}
if (contact2.url) {
ok(contact1.url != null, "conatct1.url exists");
}
checkField(contact1.url[i], contact2.url[i]);
}
for (let i in contact1.impp) {
if (contact1.impp) {
ok(contact2.impp != null, "conatct2.impp exists");
}
if (contact2.impp) {
ok(contact1.impp != null, "conatct1.impp exists");
}
checkField(contact1.impp[i], contact2.impp[i]);
}
checkArrayField(contact1.adr, contact2.adr, checkAddress, "Same adr");
checkArrayField(contact1.tel, contact2.tel, checkTel, "Same tel");
checkArrayField(contact1.email, contact2.email, checkField, "Same email");
checkArrayField(contact1.url, contact2.url, checkField, "Same url");
checkArrayField(contact1.impp, contact2.impp, checkField, "Same impp");
}
function clearDatabase() {
@ -169,24 +204,22 @@ function clearDatabase() {
function addContacts() {
ok(true, "Adding 40 contacts");
for (let i = 0; i < 39; ++i) {
createResult1 = new mozContact();
properties1.familyName[0] = "Testname" + (i < 10 ? "0" + i : i);
properties1.name = properties1.givenName[0] + " " + properties1.familyName[0];
createResult1.init(properties1);
properties1.name = [properties1.givenName[0] + " " + properties1.familyName[0]];
createResult1 = new mozContact(properties1);
req = mozContacts.save(createResult1);
req.onsuccess = function() {
ok(createResult1.id, "The contact now has an ID.");
};
req.onerror = onFailure;
};
createResult1 = new mozContact();
properties1.familyName[0] = "Testname39";
properties1.name = properties1.givenName[0] + " Testname39";
createResult1.init(properties1);
properties1.name = [properties1.givenName[0] + " Testname39"];
createResult1 = new mozContact(properties1);
req = mozContacts.save(createResult1);
req.onsuccess = function() {
ok(createResult1.id, "The contact now has an ID.");
ok(createResult1.name == properties1.name, "Same Name");
checkStrArray(createResult1.name, properties1.name, "Same Name");
next();
};
req.onerror = onFailure;
@ -202,6 +235,7 @@ function getOne(msg) {
return function() {
ok(true, msg || "Retrieving one contact with getAll");
req = mozContacts.getAll({});
let count = 0;
req.onsuccess = function(event) {
ok(true, "on success");
@ -246,11 +280,14 @@ function getAll(msg) {
}
let steps = [
function start() {
SpecialPowers.Cc["@mozilla.org/tools/profiler;1"].getService(SpecialPowers.Ci.nsIProfiler).AddMarker("GETALL_START");
next();
},
clearDatabase,
function() {
// add a contact
createResult1 = new mozContact();
createResult1.init({});
createResult1 = new mozContact({});
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function() {
next();
@ -441,6 +478,7 @@ let steps = [
function() {
ok(true, "all done!\n");
SpecialPowers.Cc["@mozilla.org/tools/profiler;1"].getService(SpecialPowers.Ci.nsIProfiler).AddMarker("GETALL_END");
SimpleTest.finish();
}
];

View File

@ -51,13 +51,13 @@ var number2 = {
};
var properties1 = {
name: "Testname1",
name: ["Testname1"],
tel: [{type: ["work"], value: number1.local, carrier: "testCarrier"} , {type: ["home", "fax"], value: number2.local}],
};
var shortNumber = "888";
var properties2 = {
name: "Testname2",
name: ["Testname2"],
tel: [{type: ["work"], value: shortNumber, carrier: "testCarrier"}]
};
@ -81,8 +81,7 @@ var steps = [
},
function () {
ok(true, "Adding a new contact1");
createResult1 = new mozContact();
createResult1.init(properties1);
createResult1 = new mozContact(properties1);
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
@ -93,8 +92,7 @@ var steps = [
},
function () {
ok(true, "Adding a new contact2");
var createResult2 = new mozContact();
createResult2.init(properties2);
var createResult2 = new mozContact(properties2);
req = navigator.mozContacts.save(createResult2);
req.onsuccess = function () {
ok(createResult2.id, "The contact now has an ID.");

View File

@ -82,8 +82,7 @@ var steps = [
},
function () {
ok(true, "Adding contact");
createResult1 = new mozContact();
createResult1.init(prop);
createResult1 = new mozContact(prop);
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
@ -166,8 +165,7 @@ var steps = [
},
function () {
ok(true, "Adding contact");
createResult1 = new mozContact();
createResult1.init(prop2);
createResult1 = new mozContact(prop2);
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
@ -259,8 +257,7 @@ var steps = [
},
function () {
ok(true, "Adding contact");
createResult1 = new mozContact();
createResult1.init(prop3);
createResult1 = new mozContact(prop3);
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
@ -289,8 +286,7 @@ var steps = [
},
function () {
ok(true, "Adding contact");
createResult1 = new mozContact();
createResult1.init(prop4);
createResult1 = new mozContact(prop4);
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");

View File

@ -73,8 +73,7 @@ var steps = [
},
function () {
ok(true, "Adding contact");
createResult1 = new mozContact();
createResult1.init(prop);
createResult1 = new mozContact(prop);
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");
@ -112,8 +111,7 @@ var steps = [
},
function () {
ok(true, "Adding contact");
createResult1 = new mozContact();
createResult1.init(prop2);
createResult1 = new mozContact(prop2);
req = navigator.mozContacts.save(createResult1);
req.onsuccess = function () {
ok(createResult1.id, "The contact now has an ID.");

View File

@ -5,7 +5,6 @@
#include "nsIDOMEventTarget.idl"
#include "SimToolKit.idl"
interface nsIDOMContact;
interface nsIDOMDOMRequest;
interface nsIDOMEventListener;
interface nsIDOMMozIccInfo;
@ -507,7 +506,7 @@ interface nsIDOMMozIccManager : nsIDOMEventTarget
* PIN2 is only required for 'fdn'.
*/
nsIDOMDOMRequest updateContact(in DOMString contactType,
in nsIDOMContact contact,
in nsISupports contact,
[optional] in DOMString pin2);
// End of UICC Phonebook Interfaces.

View File

@ -4,7 +4,6 @@
#include "nsISupports.idl"
interface nsIDOMContact;
interface nsIDOMDOMRequest;
interface nsIDOMMozIccInfo;
interface nsIDOMWindow;
@ -74,7 +73,7 @@ interface nsIIccProvider : nsISupports
nsIDOMDOMRequest updateContact(in nsIDOMWindow window,
in DOMString contactType,
in nsIDOMContact contact,
in nsISupports contact,
in DOMString pin2);
/**

View File

@ -235,7 +235,7 @@ IccManager::ReadContacts(const nsAString& aContactType, nsIDOMDOMRequest** aRequ
NS_IMETHODIMP
IccManager::UpdateContact(const nsAString& aContactType,
nsIDOMContact* aContact,
nsISupports* aContact,
const nsAString& aPin2,
nsIDOMDOMRequest** aRequest)
{

View File

@ -15,19 +15,19 @@ function testReadContacts(type) {
is(Array.isArray(contacts), true);
is(contacts[0].name, "Mozilla");
is(contacts[0].name[0], "Mozilla");
is(contacts[0].tel[0].value, "15555218201");
is(contacts[0].id, "890141032111185107201");
is(contacts[1].name, "Saßê黃");
is(contacts[1].name[0], "Saßê黃");
is(contacts[1].tel[0].value, "15555218202");
is(contacts[1].id, "890141032111185107202");
is(contacts[2].name, "Fire 火");
is(contacts[2].name[0], "Fire 火");
is(contacts[2].tel[0].value, "15555218203");
is(contacts[2].id, "890141032111185107203");
is(contacts[3].name, "Huang 黃");
is(contacts[3].name[0], "Huang 黃");
is(contacts[3].tel[0].value, "15555218204");
is(contacts[3].id, "890141032111185107204");
@ -41,10 +41,8 @@ function testReadContacts(type) {
};
function testAddContact(type, pin2) {
let contact = new mozContact();
contact.init({
name: "add",
let contact = new mozContact({
name: ["add"],
tel: [{value: "0912345678"}],
email:[]
});
@ -62,7 +60,7 @@ function testAddContact(type, pin2) {
// There are 4 SIM contacts which are harded in emulator
is(contacts.length, 5);
is(contacts[4].name, "add");
is(contacts[4].name[0], "add");
is(contacts[4].tel[0].value, "0912345678");
runNextTest();

View File

@ -5,9 +5,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
XPIDL_SOURCES += [
'nsIContactProperties.idl',
'nsIDOMContactManager.idl',
'nsIDOMMozContactChangeEvent.idl',
'nsIDOMMozContactChangeEvent.idl'
]
XPIDL_MODULE = 'dom_contacts'

View File

@ -1,74 +0,0 @@
/* 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"
[scriptable, uuid(9cbfa81c-bcab-4ca9-b0d2-f4318f295e33)]
interface nsIContactAddress : nsISupports
{
attribute DOMString type;
attribute boolean pref; // false = no pref, true = preferred (vCard3 TYPE:PREF; vCard4 PREF:1)
attribute DOMString streetAddress;
attribute DOMString locality;
attribute DOMString region;
attribute DOMString postalCode;
attribute DOMString countryName;
};
[scriptable, uuid(ad19a543-69e4-44f0-adfa-37c011556bc1)]
interface nsIContactField : nsISupports
{
attribute jsval type; // DOMString[], "home", "work", etc.
attribute DOMString value;
attribute boolean pref; // false = no pref, true = preferred (vCard3 TYPE:PREF; vCard4 PREF:1)
};
[scriptable, uuid(4d42c5a9-ea5d-4102-80c3-40cc986367ca)]
interface nsIContactTelField : nsIContactField
{
attribute DOMString carrier;
};
[scriptable, uuid(0a5b1fab-70da-46dd-b902-619904d920c2)]
interface nsIContactFindSortOptions : nsISupports
{
attribute DOMString sortBy; // "givenName" or "familyName"
attribute DOMString sortOrder; // e.g. "descending"
};
[scriptable, uuid(28ce07d0-45d9-4b7a-8843-521df4edd8bc)]
interface nsIContactFindOptions : nsIContactFindSortOptions
{
attribute DOMString filterValue; // e.g. "Tom"
attribute DOMString filterOp; // e.g. "startsWith"
attribute jsval filterBy; // DOMString[], e.g. ["givenName", "nickname"]
attribute unsigned long filterLimit;
};
[scriptable, uuid(35ad8a4e-9486-44b6-883d-550f14635e49)]
interface nsIContactProperties : nsISupports
{
attribute jsval name; // DOMString[]
attribute jsval honorificPrefix; // DOMString[]
attribute jsval givenName; // DOMString[]
attribute jsval additionalName; // DOMString[]
attribute jsval familyName; // DOMString[]
attribute jsval honorificSuffix; // DOMString[]
attribute jsval nickname; // DOMString[]
attribute jsval email; // ContactField[]
attribute jsval photo; // nsIDOMBlob[]
attribute jsval url; // ContactField[]
attribute jsval category; // DOMString[]
attribute jsval adr; // ContactAddress[]
attribute jsval tel; // ContactTelField[]
attribute jsval org; // DOMString[]
attribute jsval jobTitle; // DOMString[]
attribute jsval bday; // Date
attribute jsval note; // DOMString[]
attribute jsval impp; // ContactField[]
attribute jsval anniversary; // Date
attribute DOMString sex; // DOMString
attribute DOMString genderIdentity; // DOMString
attribute jsval key; // DOMString[]
};

View File

@ -1,41 +0,0 @@
/* 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 "nsIContactProperties.idl"
#include "nsIDOMEventTarget.idl"
interface nsIArray;
interface nsIDOMDOMRequest;
interface nsIDOMDOMCursor;
[scriptable, uuid(72a5ee28-81d8-4af8-90b3-ae935396cc66)]
interface nsIDOMContact : nsIContactProperties
{
attribute DOMString id;
readonly attribute jsval published;
readonly attribute jsval updated;
void init(in nsIContactProperties properties); // Workaround BUG 723206
};
[scriptable, uuid(8beb3a66-d70a-4111-b216-b8e995ad3aff)]
interface nsIDOMContactManager : nsISupports
{
nsIDOMDOMRequest find(in nsIContactFindOptions options);
nsIDOMDOMCursor getAll(in nsIContactFindSortOptions options);
nsIDOMDOMRequest clear();
nsIDOMDOMRequest save(in nsIDOMContact contact);
nsIDOMDOMRequest remove(in nsIDOMContact contact);
attribute nsIDOMEventListener oncontactchange;
nsIDOMDOMRequest getRevision();
nsIDOMDOMRequest getCount();
};

View File

@ -1671,7 +1671,6 @@ RILContentHelper.prototype = {
delete this._windowsMap[message.requestId];
let contacts = message.contacts;
let result = contacts.map(function(c) {
let contact = Cc["@mozilla.org/contact;1"].createInstance(Ci.nsIDOMContact);
let prop = {name: [c.alphaId], tel: [{value: c.number}]};
if (c.email) {
@ -1684,7 +1683,7 @@ RILContentHelper.prototype = {
prop.tel.push({value: c.anr[i]});
}
contact.init(prop);
let contact = new window.mozContact(prop);
contact.id = message.iccid + c.recordId;
return contact;
});

170
dom/webidl/Contacts.webidl Normal file
View File

@ -0,0 +1,170 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*/
[ChromeOnly, Constructor, JSImplementation="@mozilla.org/contactAddress;1"]
interface ContactAddress {
attribute object? type; // DOMString[]
attribute DOMString? streetAddress;
attribute DOMString? locality;
attribute DOMString? region;
attribute DOMString? postalCode;
attribute DOMString? countryName;
attribute boolean? pref;
[ChromeOnly]
void initialize(optional sequence<DOMString>? type,
optional DOMString streetAddress,
optional DOMString locality,
optional DOMString region,
optional DOMString postalCode,
optional DOMString countryName,
optional boolean pref);
};
dictionary ContactAddressInit {
sequence<DOMString>? type;
DOMString? streetAddress;
DOMString? locality;
DOMString? region;
DOMString? postalCode;
DOMString? countryName;
boolean? pref;
};
[ChromeOnly, Constructor, JSImplementation="@mozilla.org/contactField;1"]
interface ContactField {
attribute object? type; // DOMString[]
attribute DOMString? value;
attribute boolean? pref;
[ChromeOnly]
void initialize(optional sequence<DOMString>? type,
optional DOMString value,
optional boolean pref);
};
dictionary ContactFieldInit {
sequence<DOMString>? type;
DOMString? value;
boolean? pref;
};
[ChromeOnly, Constructor, JSImplementation="@mozilla.org/contactTelField;1"]
interface ContactTelField : ContactField {
attribute DOMString? carrier;
[ChromeOnly]
void initialize(optional sequence<DOMString>? type,
optional DOMString value,
optional DOMString? carrier,
optional boolean pref);
};
dictionary ContactTelFieldInit : ContactFieldInit {
DOMString? carrier;
};
dictionary ContactProperties {
Date? bday;
Date? anniversary;
DOMString? sex;
DOMString? genderIdentity;
sequence<Blob>? photo;
sequence<ContactAddressInit>? adr;
sequence<ContactFieldInit>? email;
sequence<ContactFieldInit>? url;
sequence<ContactFieldInit>? impp;
sequence<ContactTelFieldInit>? tel;
sequence<DOMString>? name;
sequence<DOMString>? honorificPrefix;
sequence<DOMString>? givenName;
sequence<DOMString>? additionalName;
sequence<DOMString>? familyName;
sequence<DOMString>? honorificSuffix;
sequence<DOMString>? nickname;
sequence<DOMString>? category;
sequence<DOMString>? org;
sequence<DOMString>? jobTitle;
sequence<DOMString>? note;
sequence<DOMString>? key;
};
[Constructor(optional ContactProperties properties),
JSImplementation="@mozilla.org/contact;1"]
interface mozContact {
attribute DOMString id;
readonly attribute Date? published;
readonly attribute Date? updated;
attribute Date? bday;
attribute Date? anniversary;
attribute DOMString? sex;
attribute DOMString? genderIdentity;
attribute object? photo;
attribute object? adr;
attribute object? email;
attribute object? url;
attribute object? impp;
attribute object? tel;
attribute object? name;
attribute object? honorificPrefix;
attribute object? givenName;
attribute object? additionalName;
attribute object? familyName;
attribute object? honorificSuffix;
attribute object? nickname;
attribute object? category;
attribute object? org;
attribute object? jobTitle;
attribute object? note;
attribute object? key;
[ChromeOnly]
void setMetadata(DOMString id, optional Date published, optional Date updated);
jsonifier;
};
dictionary ContactFindSortOptions {
DOMString sortBy; // "givenName" or "familyName"
DOMString sortOrder = "ascending"; // e.g. "descending"
};
dictionary ContactFindOptions : ContactFindSortOptions {
DOMString filterValue; // e.g. "Tom"
DOMString filterOp; // e.g. "startsWith"
any filterBy; // e.g. ["givenName", "nickname"]
unsigned long filterLimit;
};
[NoInterfaceObject, NavigatorProperty="mozContacts",
JSImplementation="@mozilla.org/contactManager;1"]
interface ContactManager : EventTarget {
DOMRequest find(optional ContactFindOptions options);
DOMCursor getAll(optional ContactFindSortOptions options);
DOMRequest clear();
DOMRequest save(mozContact contact);
DOMRequest remove(mozContact contact);
DOMRequest getRevision();
DOMRequest getCount();
attribute EventHandler oncontactchange;
};

View File

@ -53,6 +53,7 @@ WEBIDL_FILES = [
'CommandEvent.webidl',
'Comment.webidl',
'CompositionEvent.webidl',
'Contacts.webidl',
'ConvolverNode.webidl',
'Coordinates.webidl',
'CSS.webidl',