зеркало из https://github.com/mozilla/gecko-dev.git
Bug 674720 - WebContacts (or Contacts+). r=fabrice, jonas, bent, tantek
This commit is contained in:
Родитель
2a9dd6c867
Коммит
b6da0b881d
|
@ -410,6 +410,10 @@ pref("dom.mozBrowserFramesWhitelist", "http://localhost:7777");
|
|||
pref("dom.sms.enabled", true);
|
||||
pref("dom.sms.whitelist", "file://,http://localhost:7777");
|
||||
|
||||
// Temporary permission hack for WebContacts
|
||||
pref("dom.mozContacts.enabled", true);
|
||||
pref("dom.mozContacts.whitelist", "http://localhost:7777");
|
||||
|
||||
// Ignore X-Frame-Options headers.
|
||||
pref("b2g.ignoreXFrameOptions", true);
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ const LocalFile = CC('@mozilla.org/file/local;1',
|
|||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
Cu.import('resource://gre/modules/ContactService.jsm');
|
||||
|
||||
XPCOMUtils.defineLazyGetter(Services, 'env', function() {
|
||||
return Cc['@mozilla.org/process/environment;1']
|
||||
|
@ -60,7 +61,7 @@ function startupHttpd(baseDir, port) {
|
|||
// XXX never grant 'content-camera' to non-gaia apps
|
||||
function addPermissions(urls) {
|
||||
let permissions = [
|
||||
'indexedDB', 'indexedDB-unlimited', 'webapps-manage', 'offline-app', 'content-camera'
|
||||
'indexedDB', 'indexedDB-unlimited', 'webapps-manage', 'offline-app', 'content-camera', 'webcontacts-manage'
|
||||
];
|
||||
urls.forEach(function(url) {
|
||||
let uri = Services.io.newURI(url, null, null);
|
||||
|
|
|
@ -155,6 +155,7 @@
|
|||
@BINPATH@/components/dom_bluetooth.xpt
|
||||
#endif
|
||||
@BINPATH@/components/dom_canvas.xpt
|
||||
@BINPATH@/components/dom_contacts.xpt
|
||||
@BINPATH@/components/dom_core.xpt
|
||||
@BINPATH@/components/dom_css.xpt
|
||||
@BINPATH@/components/dom_events.xpt
|
||||
|
@ -294,6 +295,8 @@
|
|||
; JavaScript components
|
||||
@BINPATH@/components/ConsoleAPI.manifest
|
||||
@BINPATH@/components/ConsoleAPI.js
|
||||
@BINPATH@/components/ContactManager.js
|
||||
@BINPATH@/components/ContactManager.manifest
|
||||
@BINPATH@/components/FeedProcessor.manifest
|
||||
@BINPATH@/components/FeedProcessor.js
|
||||
@BINPATH@/components/BrowserFeeds.manifest
|
||||
|
|
|
@ -155,6 +155,7 @@
|
|||
@BINPATH@/components/dom_bluetooth.xpt
|
||||
#endif
|
||||
@BINPATH@/components/dom_canvas.xpt
|
||||
@BINPATH@/components/dom_contacts.xpt
|
||||
@BINPATH@/components/dom_core.xpt
|
||||
@BINPATH@/components/dom_css.xpt
|
||||
@BINPATH@/components/dom_events.xpt
|
||||
|
@ -409,6 +410,9 @@
|
|||
@BINPATH@/components/messageWakeupService.js
|
||||
@BINPATH@/components/messageWakeupService.manifest
|
||||
|
||||
@BINPATH@/components/ContactManager.js
|
||||
@BINPATH@/components/ContactManager.manifest
|
||||
|
||||
; Modules
|
||||
@BINPATH@/modules/*
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ MODULE = dom
|
|||
DIRS = \
|
||||
interfaces/base \
|
||||
interfaces/canvas \
|
||||
interfaces/contacts \
|
||||
interfaces/core \
|
||||
interfaces/html \
|
||||
interfaces/events \
|
||||
|
@ -77,6 +78,7 @@ endif
|
|||
DIRS += \
|
||||
base \
|
||||
battery \
|
||||
contacts \
|
||||
power \
|
||||
sms \
|
||||
src \
|
||||
|
|
|
@ -0,0 +1,404 @@
|
|||
/* 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/. */
|
||||
|
||||
"use strict"
|
||||
|
||||
/* static functions */
|
||||
let DEBUG = 0;
|
||||
if (DEBUG)
|
||||
debug = function (s) { dump("-*- ContactManager: " + s + "\n"); }
|
||||
else
|
||||
debug = function (s) {}
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const nsIClassInfo = Ci.nsIClassInfo;
|
||||
const CONTACTPROPERTIES_CID = Components.ID("{53ed7c20-ceda-11e0-9572-0800200c9a66}");
|
||||
const nsIDOMContactProperties = Ci.nsIDOMContactProperties;
|
||||
|
||||
// ContactProperties is not directly instantiated. It is used as interface.
|
||||
|
||||
ContactProperties.prototype = {
|
||||
|
||||
classID : CONTACTPROPERTIES_CID,
|
||||
classInfo : XPCOMUtils.generateCI({classID: CONTACTPROPERTIES_CID,
|
||||
contractID:"@mozilla.org/contactProperties;1",
|
||||
classDescription: "ContactProperties",
|
||||
interfaces: [nsIDOMContactProperties],
|
||||
flags: nsIClassInfo.DOM_OBJECT}),
|
||||
|
||||
QueryInterface : XPCOMUtils.generateQI([nsIDOMContactProperties])
|
||||
}
|
||||
|
||||
//ContactAddress
|
||||
|
||||
const CONTACTADDRESS_CONTRACTID = "@mozilla.org/contactAddress;1";
|
||||
const CONTACTADDRESS_CID = Components.ID("{27a568b0-cee1-11e0-9572-0800200c9a66}");
|
||||
const nsIDOMContactAddress = Components.interfaces.nsIDOMContactAddress;
|
||||
|
||||
function ContactAddress(aStreetAddress, aLocality, aRegion, aPostalCode, aCountryName) {
|
||||
this.streetAddress = aStreetAddress || null;
|
||||
this.locality = aLocality || null;
|
||||
this.region = aRegion || null;
|
||||
this.postalCode = aPostalCode || null;
|
||||
this.countryName = aCountryName || null;
|
||||
};
|
||||
|
||||
function ContactProperties(aProp) { debug("ContactProperties Constructor"); }
|
||||
|
||||
ContactAddress.prototype = {
|
||||
|
||||
classID : CONTACTADDRESS_CID,
|
||||
classInfo : XPCOMUtils.generateCI({classID: CONTACTADDRESS_CID,
|
||||
contractID: CONTACTADDRESS_CONTRACTID,
|
||||
classDescription: "ContactAddress",
|
||||
interfaces: [nsIDOMContactAddress],
|
||||
flags: nsIClassInfo.DOM_OBJECT}),
|
||||
|
||||
QueryInterface : XPCOMUtils.generateQI([nsIDOMContactAddress])
|
||||
}
|
||||
|
||||
//ContactFindOptions
|
||||
|
||||
const CONTACTFINDOPTIONS_CONTRACTID = "@mozilla.org/contactFindOptions;1";
|
||||
const CONTACTFINDOPTIONS_CID = Components.ID("{e31daea0-0cb6-11e1-be50-0800200c9a66}");
|
||||
const nsIDOMContactFindOptions = Components.interfaces.nsIDOMContactFindOptions;
|
||||
|
||||
function ContactFindOptions(aFilterValue, aFilterBy, aFilterOp, aFilterLimit) {
|
||||
this.filterValue = aFilterValue || '';
|
||||
|
||||
this.filterBy = new Array();
|
||||
for (let field in aFilterBy)
|
||||
this.filterBy.push(field);
|
||||
|
||||
this.filterOp = aFilterOp || '';
|
||||
this.filterLimit = aFilterLimit || 0;
|
||||
};
|
||||
|
||||
ContactFindOptions.prototype = {
|
||||
|
||||
classID : CONTACTFINDOPTIONS_CID,
|
||||
classInfo : XPCOMUtils.generateCI({classID: CONTACTFINDOPTIONS_CID,
|
||||
contractID: CONTACTFINDOPTIONS_CONTRACTID,
|
||||
classDescription: "ContactFindOptions",
|
||||
interfaces: [nsIDOMContactFindOptions],
|
||||
flags: nsIClassInfo.DOM_OBJECT}),
|
||||
|
||||
QueryInterface : XPCOMUtils.generateQI([nsIDOMContactFindOptions])
|
||||
}
|
||||
|
||||
//Contact
|
||||
|
||||
const CONTACT_CONTRACTID = "@mozilla.org/contact;1";
|
||||
const CONTACT_CID = Components.ID("{da0f7040-388b-11e1-b86c-0800200c9a66}");
|
||||
const nsIDOMContact = Components.interfaces.nsIDOMContact;
|
||||
|
||||
function Contact() { debug("Contact constr: "); };
|
||||
|
||||
Contact.prototype = {
|
||||
|
||||
init: function init(aProp) {
|
||||
// Accept non-array strings for DOMString[] properties and convert them.
|
||||
function _create(aField) {
|
||||
if (typeof aField == "string")
|
||||
return new Array(aField);
|
||||
return aField;
|
||||
};
|
||||
|
||||
this.name = _create(aProp.name) || null;
|
||||
this.honorificPrefix = _create(aProp.honorificPrefix) || null;
|
||||
this.givenName = _create(aProp.givenName) || null;
|
||||
this.additionalName = _create(aProp.additionalName) || null;
|
||||
this.familyName = _create(aProp.familyName) || null;
|
||||
this.honorificSuffix = _create(aProp.honorificSuffix) || null;
|
||||
this.nickname = _create(aProp.nickname) || null;
|
||||
this.email = _create(aProp.email) || null;
|
||||
this.photo = _create(aProp.photo) || null;
|
||||
this.url = _create(aProp.url) || null;
|
||||
this.category = _create(aProp.category) || null;
|
||||
|
||||
if (aProp.adr) {
|
||||
// Make sure adr argument is an array. Instanceof doesn't work.
|
||||
aProp.adr = aProp.adr.length == undefined ? [aProp.adr] : aProp.adr;
|
||||
|
||||
this.adr = new Array();
|
||||
for (let i = 0; i < aProp.adr.length; i++)
|
||||
this.adr.push(new ContactAddress(aProp.adr[i].streetAddress, aProp.adr[i].locality,
|
||||
aProp.adr[i].region, aProp.adr[i].postalCode,
|
||||
aProp.adr[i].countryName));
|
||||
} else {
|
||||
this.adr = null;
|
||||
}
|
||||
|
||||
this.tel = _create(aProp.tel) || null;
|
||||
this.org = _create(aProp.org) || null;
|
||||
this.bday = (aProp.bday == "undefined" || aProp.bday == null) ? null : new Date(aProp.bday);
|
||||
this.note = _create(aProp.note) || null;
|
||||
this.impp = _create(aProp.impp) || null;
|
||||
this.anniversary = (aProp.anniversary == "undefined" || aProp.anniversary == null) ? null : new Date(aProp.anniversary);
|
||||
this.sex = (aProp.sex != "undefined") ? aProp.sex : null;
|
||||
this.genderIdentity = (aProp.genderIdentity != "undefined") ? aProp.genderIdentity : null;
|
||||
},
|
||||
|
||||
get published () {
|
||||
return this._published;
|
||||
},
|
||||
|
||||
set published(aPublished) {
|
||||
this._published = aPublished;
|
||||
},
|
||||
|
||||
get updated () {
|
||||
return this._updated;
|
||||
},
|
||||
|
||||
set updated(aUpdated) {
|
||||
this._updated = aUpdated;
|
||||
},
|
||||
|
||||
classID : CONTACT_CID,
|
||||
classInfo : XPCOMUtils.generateCI({classID: CONTACT_CID,
|
||||
contractID: CONTACT_CONTRACTID,
|
||||
classDescription: "Contact",
|
||||
interfaces: [nsIDOMContact, nsIDOMContactProperties],
|
||||
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 nsIDOMContactManager = Components.interfaces.nsIDOMContactManager;
|
||||
|
||||
function ContactManager()
|
||||
{
|
||||
debug("Constructor");
|
||||
}
|
||||
|
||||
ContactManager.prototype = {
|
||||
|
||||
save: function save(aContact) {
|
||||
let request;
|
||||
if (this.hasPrivileges) {
|
||||
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: [],
|
||||
bday: null,
|
||||
note: [],
|
||||
impp: [],
|
||||
anniversary: null,
|
||||
sex: null,
|
||||
genderIdentity: null
|
||||
};
|
||||
for (let field in newContact.properties)
|
||||
newContact.properties[field] = aContact[field];
|
||||
|
||||
if (aContact.id == "undefined") {
|
||||
debug("Create id!");
|
||||
aContact.id = this._getRandomId();
|
||||
}
|
||||
|
||||
this._setMetaData(newContact, aContact);
|
||||
debug("send: " + JSON.stringify(newContact));
|
||||
request = this._rs.createRequest(this._window);
|
||||
this._mm.sendAsyncMessage("Contact:Save", {contact: newContact,
|
||||
requestID: this.getRequestId({ request: request })});
|
||||
return request;
|
||||
} else {
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
},
|
||||
|
||||
remove: function removeContact(aRecord) {
|
||||
let request;
|
||||
if (this.hasPrivileges) {
|
||||
request = this._rs.createRequest(this._window);
|
||||
this._mm.sendAsyncMessage("Contact:Remove", {id: aRecord.id,
|
||||
requestID: this.getRequestId({ request: request })});
|
||||
return request;
|
||||
} else {
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
},
|
||||
|
||||
_setMetaData: function(aNewContact, aRecord) {
|
||||
aNewContact.id = aRecord.id;
|
||||
aNewContact.published = aRecord.published;
|
||||
aNewContact.updated = aRecord.updated;
|
||||
},
|
||||
|
||||
_convertContactsArray: function(aContacts) {
|
||||
let contacts = new Array();
|
||||
for (let i in aContacts) {
|
||||
let newContact = new Contact();
|
||||
newContact.init(aContacts[i].properties);
|
||||
this._setMetaData(newContact, aContacts[i]);
|
||||
contacts.push(newContact);
|
||||
}
|
||||
return contacts;
|
||||
},
|
||||
|
||||
getRequestId: function(aRequest) {
|
||||
let id = "id" + this._getRandomId();
|
||||
this._requests[id] = aRequest;
|
||||
return id;
|
||||
},
|
||||
|
||||
getRequest: function(aId) {
|
||||
if (this._requests[aId])
|
||||
return this._requests[aId].request;
|
||||
},
|
||||
|
||||
removeRequest: function(aId) {
|
||||
if (this._requests[aId])
|
||||
delete this._requests[aId];
|
||||
},
|
||||
|
||||
_getRandomId: function() {
|
||||
return Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString();
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
debug("Contactmanager::receiveMessage: " + aMessage.name);
|
||||
let msg = aMessage.json;
|
||||
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));
|
||||
this._rs.fireSuccess(req, 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)
|
||||
this._rs.fireSuccess(req, 0);
|
||||
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)
|
||||
this._rs.fireError(req, msg.errorMsg);
|
||||
break;
|
||||
default:
|
||||
debug("Wrong message: " + aMessage.name);
|
||||
}
|
||||
this.removeRequest(msg.requestID);
|
||||
},
|
||||
|
||||
find: function(aOptions) {
|
||||
let request;
|
||||
if (this.hasPrivileges) {
|
||||
request = this._rs.createRequest(this._window);
|
||||
this._mm.sendAsyncMessage("Contacts:Find", {findOptions: aOptions,
|
||||
requestID: this.getRequestId({ request: request })});
|
||||
return request;
|
||||
} else {
|
||||
debug("find not allowed");
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
let request;
|
||||
if (this.hasPrivileges) {
|
||||
request = this._rs.createRequest(this._window);
|
||||
this._mm.sendAsyncMessage("Contacts:Clear", {requestID: this.getRequestId({ request: request })});
|
||||
return request;
|
||||
} else {
|
||||
debug("clear not allowed");
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
},
|
||||
|
||||
init: function(aWindow) {
|
||||
// Set navigator.mozContacts to null.
|
||||
if (!Services.prefs.getBoolPref("dom.mozContacts.enabled"))
|
||||
return null;
|
||||
|
||||
this._window = aWindow;
|
||||
this._messages = ["Contacts:Find:Return:OK", "Contacts:Find:Return:KO",
|
||||
"Contacts:Clear:Return:OK", "Contacts:Clear:Return:KO",
|
||||
"Contact:Save:Return:OK", "Contact:Save:Return:KO",
|
||||
"Contact:Remove:Return:OK", "Contact:Remove:Return:KO"];
|
||||
|
||||
this._mm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
|
||||
this._messages.forEach((function(msgName) {
|
||||
this._mm.addMessageListener(msgName, this);
|
||||
}).bind(this));
|
||||
|
||||
this._rs = Cc["@mozilla.org/dom/dom-request-service;1"].getService(Ci.nsIDOMRequestService);
|
||||
this._requests = [];
|
||||
Services.obs.addObserver(this, "inner-window-destroyed", false);
|
||||
let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
||||
this._innerWindowID = util.currentInnerWindowID;
|
||||
|
||||
let principal = aWindow.document.nodePrincipal;
|
||||
let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager);
|
||||
|
||||
let perm = principal == secMan.getSystemPrincipal() ?
|
||||
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);
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
|
||||
if (wId == this.innerWindowID) {
|
||||
Services.obs.removeObserver(this, "inner-window-destroyed");
|
||||
this._messages.forEach((function(msgName) {
|
||||
this._mm.removeMessageListener(msgName, this);
|
||||
}).bind(this));
|
||||
this._mm = null;
|
||||
this._messages = null;
|
||||
this._requests = null;
|
||||
this._window = null;
|
||||
this._innerWindowID = 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})
|
||||
}
|
||||
|
||||
const NSGetFactory = XPCOMUtils.generateNSGetFactory([Contact, ContactManager, ContactProperties, ContactAddress, ContactFindOptions])
|
|
@ -0,0 +1,16 @@
|
|||
component {53ed7c20-ceda-11e0-9572-0800200c9a66} ContactManager.js
|
||||
contract @mozilla.org/contactProperties;1 {53ed7c20-ceda-11e0-9572-0800200c9a66}
|
||||
|
||||
component {27a568b0-cee1-11e0-9572-0800200c9a66} ContactManager.js
|
||||
contract @mozilla.org/contactAddress;1 {27a568b0-cee1-11e0-9572-0800200c9a66}
|
||||
|
||||
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}
|
||||
category JavaScript-navigator-property mozContacts @mozilla.org/contactManager;1
|
|
@ -0,0 +1,47 @@
|
|||
# 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/.
|
||||
|
||||
DEPTH = ../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = \
|
||||
$(srcdir) \
|
||||
$(NULL)
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
ifeq ($(MOZ_WIDGET_TOOLKIT),gonk)
|
||||
VPATH += $(srcdir)/fallback
|
||||
endif
|
||||
|
||||
MODULE = dom
|
||||
LIBRARY_NAME = jsdomcontacts_s
|
||||
LIBXUL_LIBRARY = 1
|
||||
|
||||
EXTRA_COMPONENTS = \
|
||||
ContactManager.js \
|
||||
ContactManager.manifest \
|
||||
$(NULL)
|
||||
|
||||
ifeq ($(MOZ_WIDGET_TOOLKIT),gonk)
|
||||
EXTRA_JS_MODULES = ContactService.jsm \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_JS_MODULES += ContactDB.jsm \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
ifdef ENABLE_TESTS
|
||||
DIRS += tests
|
||||
endif
|
||||
|
||||
# Add VPATH to LOCAL_INCLUDES so we are going to include the correct backend
|
||||
# subdirectory (and the ipc one).
|
||||
LOCAL_INCLUDES += $(VPATH:%=-I%)
|
||||
|
||||
include $(topsrcdir)/config/config.mk
|
||||
include $(topsrcdir)/ipc/chromium/chromium-config.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
DEFINES += -D_IMPL_NS_LAYOUT
|
|
@ -0,0 +1,410 @@
|
|||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const EXPORTED_SYMBOLS = ['ContactDB'];
|
||||
|
||||
let DEBUG = 0;
|
||||
/* static functions */
|
||||
if (DEBUG)
|
||||
debug = function (s) { dump("-*- ContactDB component: " + s + "\n"); }
|
||||
else
|
||||
debug = function (s) {}
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const DB_NAME = "contacts";
|
||||
const DB_VERSION = 1;
|
||||
const STORE_NAME = "contacts";
|
||||
|
||||
function ContactDB(aGlobal) {
|
||||
debug("Constructor");
|
||||
this._indexedDB = aGlobal.mozIndexedDB;
|
||||
}
|
||||
|
||||
ContactDB.prototype = {
|
||||
|
||||
// Cache the DB
|
||||
db: null,
|
||||
|
||||
close: function close() {
|
||||
debug("close");
|
||||
if (this.db)
|
||||
this.db.close();
|
||||
},
|
||||
|
||||
/**
|
||||
* Prepare the database. This may include opening the database and upgrading
|
||||
* it to the latest schema version.
|
||||
*
|
||||
* @return (via callback) a database ready for use.
|
||||
*/
|
||||
ensureDB: function ensureDB(callback, failureCb) {
|
||||
if (this.db) {
|
||||
debug("ensureDB: already have a database, returning early.");
|
||||
callback(this.db);
|
||||
return;
|
||||
}
|
||||
|
||||
let self = this;
|
||||
debug("try to open database:" + DB_NAME + " " + DB_VERSION);
|
||||
let request = this._indexedDB.open(DB_NAME, DB_VERSION);
|
||||
request.onsuccess = function (event) {
|
||||
debug("Opened database:", DB_NAME, DB_VERSION);
|
||||
self.db = event.target.result;
|
||||
self.db.onversionchange = function(event) {
|
||||
debug("WARNING: DB modified from a different window.");
|
||||
}
|
||||
callback(self.db);
|
||||
};
|
||||
request.onupgradeneeded = function (event) {
|
||||
debug("Database needs upgrade:" + DB_NAME + event.oldVersion + event.newVersion);
|
||||
debug("Correct new database version:" + event.newVersion == DB_VERSION);
|
||||
|
||||
let db = event.target.result;
|
||||
switch (event.oldVersion) {
|
||||
case 0:
|
||||
debug("New database");
|
||||
self.createSchema(db);
|
||||
break;
|
||||
|
||||
default:
|
||||
debug("No idea what to do with old database version:" + event.oldVersion);
|
||||
failureCb(event.target.errorMessage);
|
||||
break;
|
||||
}
|
||||
};
|
||||
request.onerror = function (event) {
|
||||
debug("Failed to open database:", DB_NAME);
|
||||
failureCb(event.target.errorMessage);
|
||||
};
|
||||
request.onblocked = function (event) {
|
||||
debug("Opening database request is blocked.");
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Create the initial database schema.
|
||||
*
|
||||
* The schema of records stored is as follows:
|
||||
*
|
||||
* {id: "...", // UUID
|
||||
* published: Date(...), // First published date.
|
||||
* updated: Date(...), // Last updated date.
|
||||
* properties: {...} // Object holding the ContactProperties
|
||||
* }
|
||||
*/
|
||||
createSchema: function createSchema(db) {
|
||||
let objectStore = db.createObjectStore(STORE_NAME, {keyPath: "id"});
|
||||
|
||||
// Metadata indexes
|
||||
objectStore.createIndex("published", "published", { unique: false });
|
||||
objectStore.createIndex("updated", "updated", { unique: false });
|
||||
|
||||
// Properties indexes
|
||||
objectStore.createIndex("nickname", "properties.nickname", { unique: false, multiEntry: true });
|
||||
objectStore.createIndex("name", "properties.name", { unique: false, multiEntry: true });
|
||||
objectStore.createIndex("familyName", "properties.familyName", { unique: false, multiEntry: true });
|
||||
objectStore.createIndex("givenName", "properties.givenName", { unique: false, multiEntry: true });
|
||||
objectStore.createIndex("tel", "properties.tel", { unique: false, multiEntry: true });
|
||||
objectStore.createIndex("email", "properties.email", { unique: false, multiEntry: true });
|
||||
objectStore.createIndex("note", "properties.note", { unique: false, multiEntry: true });
|
||||
|
||||
debug("Created object stores and indexes");
|
||||
},
|
||||
|
||||
/**
|
||||
* Start a new transaction.
|
||||
*
|
||||
* @param txn_type
|
||||
* Type of transaction (e.g. IDBTransaction.READ_WRITE)
|
||||
* @param callback
|
||||
* Function to call when the transaction is available. It will
|
||||
* be invoked with the transaction and the 'contacts' object store.
|
||||
* @param successCb [optional]
|
||||
* Success callback to call on a successful transaction commit.
|
||||
* @param failureCb [optional]
|
||||
* Error callback to call when an error is encountered.
|
||||
*/
|
||||
newTxn: function newTxn(txn_type, callback, successCb, failureCb) {
|
||||
this.ensureDB(function (db) {
|
||||
debug("Starting new transaction" + txn_type);
|
||||
let txn = db.transaction(STORE_NAME, txn_type);
|
||||
debug("Retrieving object store", STORE_NAME);
|
||||
let store = txn.objectStore(STORE_NAME);
|
||||
|
||||
txn.oncomplete = function (event) {
|
||||
debug("Transaction complete. Returning to callback.");
|
||||
successCb(txn.result);
|
||||
};
|
||||
|
||||
txn.onabort = function (event) {
|
||||
debug("Caught error on transaction" + event.target.errorCode);
|
||||
switch(event.target.errorCode) {
|
||||
case Ci.nsIIDBDatabaseException.ABORT_ERR:
|
||||
case Ci.nsIIDBDatabaseException.CONSTRAINT_ERR:
|
||||
case Ci.nsIIDBDatabaseException.DATA_ERR:
|
||||
case Ci.nsIIDBDatabaseException.TRANSIENT_ERR:
|
||||
case Ci.nsIIDBDatabaseException.NOT_ALLOWED_ERR:
|
||||
case Ci.nsIIDBDatabaseException.NOT_FOUND_ERR:
|
||||
case Ci.nsIIDBDatabaseException.QUOTA_ERR:
|
||||
case Ci.nsIIDBDatabaseException.READ_ONLY_ERR:
|
||||
case Ci.nsIIDBDatabaseException.TIMEOUT_ERR:
|
||||
case Ci.nsIIDBDatabaseException.TRANSACTION_INACTIVE_ERR:
|
||||
case Ci.nsIIDBDatabaseException.VERSION_ERR:
|
||||
case Ci.nsIIDBDatabaseException.UNKNOWN_ERR:
|
||||
failureCb("UnknownError");
|
||||
break;
|
||||
default:
|
||||
debug("Unknown errorCode", event.target.errorCode);
|
||||
failureCb("UnknownError");
|
||||
break;
|
||||
}
|
||||
};
|
||||
callback(txn, store);
|
||||
}, failureCb);
|
||||
},
|
||||
|
||||
// Todo: add searchfields. "Tom" should be a result with T, t, To, to...
|
||||
makeImport: function makeImport(aContact) {
|
||||
let contact = {};
|
||||
contact.properties = {
|
||||
name: [],
|
||||
honorificPrefix: [],
|
||||
givenName: [],
|
||||
additionalName: [],
|
||||
familyName: [],
|
||||
honorificSuffix: [],
|
||||
nickname: [],
|
||||
email: [],
|
||||
photo: [],
|
||||
url: [],
|
||||
category: [],
|
||||
adr: [],
|
||||
tel: [],
|
||||
org: [],
|
||||
bday: null,
|
||||
note: [],
|
||||
impp: [],
|
||||
anniversary: null,
|
||||
sex: null,
|
||||
genderIdentity: null
|
||||
};
|
||||
|
||||
for (let field in aContact.properties) {
|
||||
contact.properties[field] = aContact.properties[field];
|
||||
}
|
||||
|
||||
contact.updated = aContact.updated;
|
||||
contact.published = aContact.published;
|
||||
contact.id = aContact.id;
|
||||
|
||||
return contact;
|
||||
},
|
||||
|
||||
// Needed to remove searchfields
|
||||
makeExport: function makeExport(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;
|
||||
},
|
||||
|
||||
updateRecordMetadata: function updateRecordMetadata(record) {
|
||||
if (!record.id) {
|
||||
Cu.reportError("Contact without ID");
|
||||
}
|
||||
if (!record.published) {
|
||||
record.published = new Date();
|
||||
}
|
||||
record.updated = new Date();
|
||||
},
|
||||
|
||||
saveContact: function saveContact(aContact, successCb, errorCb) {
|
||||
let contact = this.makeImport(aContact);
|
||||
this.newTxn(Ci.nsIIDBTransaction.READ_WRITE, function (txn, store) {
|
||||
debug("Going to update" + JSON.stringify(contact));
|
||||
|
||||
// Look up the existing record and compare the update timestamp.
|
||||
// If no record exists, just add the new entry.
|
||||
let newRequest = store.get(contact.id);
|
||||
newRequest.onsuccess = function (event) {
|
||||
if (!event.target.result) {
|
||||
debug("new record!")
|
||||
this.updateRecordMetadata(contact);
|
||||
store.put(contact);
|
||||
} else {
|
||||
debug("old record!")
|
||||
if (new Date(typeof contact.updated === "undefined" ? 0 : contact.updated) < new Date(event.target.result.updated)) {
|
||||
debug("rev check fail!");
|
||||
txn.abort();
|
||||
return;
|
||||
} else {
|
||||
debug("rev check OK");
|
||||
contact.published = event.target.result.published;
|
||||
contact.updated = new Date();
|
||||
store.put(contact);
|
||||
}
|
||||
}
|
||||
}.bind(this);
|
||||
}.bind(this), successCb, errorCb);
|
||||
},
|
||||
|
||||
removeContact: function removeContact(aId, aSuccessCb, aErrorCb) {
|
||||
this.newTxn(Ci.nsIIDBTransaction.READ_WRITE, function (txn, store) {
|
||||
debug("Going to delete" + aId);
|
||||
store.delete(aId);
|
||||
}, aSuccessCb, aErrorCb);
|
||||
},
|
||||
|
||||
clear: function clear(aSuccessCb, aErrorCb) {
|
||||
this.newTxn(Ci.nsIIDBTransaction.READ_WRITE, function (txn, store) {
|
||||
debug("Going to clear all!");
|
||||
store.clear();
|
||||
}, aSuccessCb, aErrorCb);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param successCb
|
||||
* Callback function to invoke with result array.
|
||||
* @param failureCb [optional]
|
||||
* Callback function to invoke when there was an error.
|
||||
* @param options [optional]
|
||||
* Object specifying search options. Possible attributes:
|
||||
* - filterBy
|
||||
* - filterOp
|
||||
* - filterValue
|
||||
* - count
|
||||
* Possibly supported in the future:
|
||||
* - fields
|
||||
* - sortBy
|
||||
* - sortOrder
|
||||
* - startIndex
|
||||
*/
|
||||
find: function find(aSuccessCb, aFailureCb, aOptions) {
|
||||
debug("ContactDB:find val:" + aOptions.filterValue + " by: " + aOptions.filterBy + " op: " + aOptions.filterOp + "\n");
|
||||
|
||||
let self = this;
|
||||
this.newTxn(Ci.nsIIDBTransaction.READ_ONLY, function (txn, store) {
|
||||
if (aOptions && aOptions.filterOp == "equals") {
|
||||
self._findWithIndex(txn, store, aOptions);
|
||||
} else if (aOptions && aOptions.filterBy) {
|
||||
self._findWithSearch(txn, store, aOptions);
|
||||
} else {
|
||||
self._findAll(txn, store, aOptions);
|
||||
}
|
||||
}, aSuccessCb, aFailureCb);
|
||||
},
|
||||
|
||||
_findWithIndex: function _findWithIndex(txn, store, options) {
|
||||
debug("_findWithIndex: " + options.filterValue +" " + options.filterOp + " " + options.filterBy + " ");
|
||||
let fields = options.filterBy;
|
||||
for (let key in fields) {
|
||||
debug("key: " + fields[key]);
|
||||
if (!store.indexNames.contains(fields[key]) && !fields[key] == "id") {
|
||||
debug("Key not valid!" + fields[key] + ", " + store.indexNames);
|
||||
txn.abort();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// lookup for all keys
|
||||
if (options.filterBy.length == 0) {
|
||||
debug("search in all fields!" + JSON.stringify(store.indexNames));
|
||||
for(let myIndex = 0; myIndex < store.indexNames.length; myIndex++) {
|
||||
fields = Array.concat(fields, store.indexNames[myIndex])
|
||||
}
|
||||
}
|
||||
|
||||
let filter_keys = fields.slice();
|
||||
for (let key = filter_keys.shift(); key; key = filter_keys.shift()) {
|
||||
let request;
|
||||
if (key == "id") {
|
||||
// store.get would return an object and not an array
|
||||
request = store.getAll(options.filterValue);
|
||||
} else {
|
||||
debug("Getting index: " + key);
|
||||
let index = store.index(key);
|
||||
request = index.getAll(options.filterValue, options.filterLimit);
|
||||
}
|
||||
if (!txn.result)
|
||||
txn.result = {};
|
||||
|
||||
request.onsuccess = function (event) {
|
||||
debug("Request successful. Record count:" + event.target.result.length);
|
||||
for (let i in event.target.result)
|
||||
txn.result[event.target.result[i].id] = this.makeExport(event.target.result[i]);
|
||||
}.bind(this);
|
||||
}
|
||||
},
|
||||
|
||||
// Will be replaced by _findWithIndex once all searchfields are added.
|
||||
_findWithSearch: function _findWithSearch(txn, store, options) {
|
||||
debug("_findWithSearch:" + options.filterValue + options.filterOp)
|
||||
store.getAll().onsuccess = function (event) {
|
||||
debug("Request successful." + event.target.result);
|
||||
txn.result = event.target.result.filter(function (record) {
|
||||
let properties = record.properties;
|
||||
for (let i = 0; i < options.filterBy.length; i++) {
|
||||
let field = options.filterBy[i];
|
||||
if (!properties[field])
|
||||
continue;
|
||||
let value = '';
|
||||
switch (field) {
|
||||
case "name":
|
||||
case "familyName":
|
||||
case "givenName":
|
||||
case "nickname":
|
||||
case "email":
|
||||
case "tel":
|
||||
case "note":
|
||||
value = [f for each (f in [properties[field]])].join("\n") || '';
|
||||
break;
|
||||
default:
|
||||
value = properties[field];
|
||||
debug("unknown field: " + field);
|
||||
}
|
||||
let match = false;
|
||||
switch (options.filterOp) {
|
||||
case "icontains":
|
||||
match = value.toLowerCase().indexOf(options.filterValue.toLowerCase()) != -1;
|
||||
break;
|
||||
case "contains":
|
||||
match = value.indexOf(options.filterValue) != -1;
|
||||
break;
|
||||
case "equals":
|
||||
match = value == options.filterValue;
|
||||
break
|
||||
}
|
||||
if (match)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}).map(this.makeExport.bind(this));
|
||||
}.bind(this);
|
||||
},
|
||||
|
||||
_findAll: function _findAll(txn, store, options) {
|
||||
debug("ContactDB:_findAll: " + JSON.stringify(options));
|
||||
if (!txn.result)
|
||||
txn.result = {};
|
||||
|
||||
store.getAll(null, options.filterLimit).onsuccess = function (event) {
|
||||
debug("Request successful. Record count:", event.target.result.length);
|
||||
for (let i in event.target.result)
|
||||
txn.result[event.target.result[i].id] = this.makeExport(event.target.result[i]);
|
||||
}.bind(this);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,96 @@
|
|||
/* 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/. */
|
||||
|
||||
"use strict"
|
||||
|
||||
let DEBUG = 0;
|
||||
if (DEBUG)
|
||||
debug = function (s) { dump("-*- Fallback ContactService component: " + s + "\n"); }
|
||||
else
|
||||
debug = function (s) {}
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
let EXPORTED_SYMBOLS = ["DOMContactManager"];
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/ContactDB.jsm");
|
||||
|
||||
let myGlobal = this;
|
||||
|
||||
let DOMContactManager = {
|
||||
|
||||
init: function() {
|
||||
debug("Init");
|
||||
this._mm = Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
|
||||
this._messages = ["Contacts:Find", "Contacts:Clear", "Contact:Save", "Contact:Remove"];
|
||||
this._messages.forEach((function(msgName) {
|
||||
this._mm.addMessageListener(msgName, this);
|
||||
}).bind(this));
|
||||
|
||||
var idbManager = Components.classes["@mozilla.org/dom/indexeddb/manager;1"].getService(Ci.nsIIndexedDatabaseManager);
|
||||
idbManager.initWindowless(myGlobal);
|
||||
this._db = new ContactDB(myGlobal);
|
||||
|
||||
Services.obs.addObserver(this, "profile-before-change", false);
|
||||
|
||||
try {
|
||||
let hosts = Services.prefs.getCharPref("dom.mozContacts.whitelist")
|
||||
hosts.split(",").forEach(function(aHost) {
|
||||
debug("Add host: " + JSON.stringify(aHost));
|
||||
if (aHost.length > 0)
|
||||
Services.perms.add(Services.io.newURI(aHost, null, null), "webcontacts-manage",
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
});
|
||||
} catch(e) { debug(e); }
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
myGlobal = null;
|
||||
this._messages.forEach((function(msgName) {
|
||||
this._mm.removeMessageListener(msgName, this);
|
||||
}).bind(this));
|
||||
Services.obs.removeObserver(this, "profile-before-change");
|
||||
this._mm = null;
|
||||
this._messages = null;
|
||||
if (this._db)
|
||||
this._db.close();
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
debug("Fallback DOMContactManager::receiveMessage " + aMessage.name);
|
||||
let msg = aMessage.json;
|
||||
switch (aMessage.name) {
|
||||
case "Contacts:Find":
|
||||
let result = new Array();
|
||||
this._db.find(
|
||||
function(contacts) {
|
||||
for (let i in contacts)
|
||||
result.push(contacts[i]);
|
||||
debug("result:" + JSON.stringify(result));
|
||||
this._mm.sendAsyncMessage("Contacts:Find:Return:OK", {requestID: msg.requestID, contacts: result});
|
||||
}.bind(this),
|
||||
function(aErrorMsg) { this._mm.sendAsyncMessage("Contacts:Find:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }) }.bind(this),
|
||||
msg.findOptions);
|
||||
break;
|
||||
case "Contact:Save":
|
||||
this._db.saveContact(msg.contact, function() {this._mm.sendAsyncMessage("Contact:Save:Return:OK", { requestID: msg.requestID }); }.bind(this),
|
||||
function(aErrorMsg) { this._mm.sendAsyncMessage("Contact:Save:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this));
|
||||
break;
|
||||
case "Contact:Remove":
|
||||
this._db.removeContact(msg.id,
|
||||
function() {this._mm.sendAsyncMessage("Contact:Remove:Return:OK", { requestID: msg.requestID }); }.bind(this),
|
||||
function(aErrorMsg) {this._mm.sendAsyncMessage("Contact:Remove:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this));
|
||||
break;
|
||||
case "Contacts:Clear":
|
||||
this._db.clear(function() { this._mm.sendAsyncMessage("Contacts:Clear:Return:OK", { requestID: msg.requestID }); }.bind(this),
|
||||
function(aErrorMsg) { this._mm.sendAsyncMessage("Contacts:Clear:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DOMContactManager.init();
|
|
@ -0,0 +1,28 @@
|
|||
# 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/.
|
||||
|
||||
DEPTH = ../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
relativesrcdir = dom/contacts/tests
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
DIRS = \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_TEST_FILES = \
|
||||
test_contacts_basics.html \
|
||||
$(NULL)
|
||||
|
||||
_CHROME_TEST_FILES = \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_TEST_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
|
||||
|
|
@ -0,0 +1,666 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id={674720}
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug {674720} WebContacts</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id={674720}">Mozilla Bug {674720}</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
"use strict"
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
Components.classes["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Components.interfaces.nsIPermissionManager)
|
||||
.add(SpecialPowers.getDocumentURIObject(window.document),
|
||||
"webcontacts-manage",
|
||||
Components.interfaces.nsIPermissionManager.ALLOW_ACTION);
|
||||
|
||||
var adr1 = {
|
||||
streetAddress: "street 1",
|
||||
locality: "locality 1",
|
||||
region: "region 1",
|
||||
postalCode: "postal code 1",
|
||||
countryName: "country 1"
|
||||
};
|
||||
|
||||
var adr2 = {
|
||||
streetAddress: "street2",
|
||||
locality: "locality2",
|
||||
region: "region2",
|
||||
postalCode: "postal code2",
|
||||
countryName: "country2"
|
||||
};
|
||||
|
||||
var properties1 = {
|
||||
name: "Testname1",
|
||||
familyName: "TestFamilyName",
|
||||
givenName: ["Test1","Test2"],
|
||||
nickname: "nicktest",
|
||||
tel: ["123456"],
|
||||
adr: adr1
|
||||
};
|
||||
|
||||
var properties2 = {
|
||||
name: ["dummyName", "dummyName2"],
|
||||
familyName: "dummyFamilyName",
|
||||
givenName: "dummyGivenName",
|
||||
honorificPrefix: ["dummyHonorificPrefix","dummyHonorificPrefix2"],
|
||||
honorificSuffix: "dummyHonorificSuffix",
|
||||
additionalName: "dummyadditionalName",
|
||||
nickname: "dummyNickname",
|
||||
tel: ["123456789", "234567890"],
|
||||
email: ["a@b.c", "b@c.d"],
|
||||
adr: [adr1, adr2],
|
||||
impp: ["im1", "im2"],
|
||||
org: ["org1", "org2"],
|
||||
bday: new Date("1980, 12, 01"),
|
||||
note: "test note",
|
||||
photo: ["pic1", "pic2"],
|
||||
category: ["cat1", "cat2"],
|
||||
url: ["www.1.com", "www.2.com"],
|
||||
anniversary: new Date("2000, 12, 01"),
|
||||
sex: "male",
|
||||
genderIdentity: "test"
|
||||
};
|
||||
|
||||
var sample_id1;
|
||||
var sample_id2;
|
||||
|
||||
var createResult1;
|
||||
var createResult2;
|
||||
|
||||
var findResult1;
|
||||
var findResult2;
|
||||
|
||||
function clearTemps() {
|
||||
sample_id1 = null;
|
||||
sample_id2 = null;
|
||||
createResult1 = null;
|
||||
createResult2 = null;
|
||||
findResult1 = null;
|
||||
findResult2 = null;
|
||||
}
|
||||
|
||||
function onUnwantedSuccess() {
|
||||
ok(false, "onUnwantedSuccess: shouldn't get here");
|
||||
}
|
||||
|
||||
function onFailure() {
|
||||
ok(false, "in on Failure!");
|
||||
}
|
||||
|
||||
function checkStr(str1, str2, msg) {
|
||||
if (str1)
|
||||
ok(typeof str1 == "string" ? [str1] : str1, (typeof str2 == "string") ? [str2] : str2, msg);
|
||||
}
|
||||
|
||||
function checkAddress(adr1, adr2) {
|
||||
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 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.email, contact2.email, "Same email");
|
||||
checkStr(contact1.photo, contact2.photo, "Same photo");
|
||||
checkStr(contact1.url, contact2.url, "Same url");
|
||||
checkStr(contact1.category, contact2.category, "Same category");
|
||||
is(contact1.bday ? contact1.bday.valueOf() : null, contact2.bday ? contact2.bday.valueOf() : null, "Same birthday");
|
||||
checkStr(contact1.note, contact2.note, "Same note");
|
||||
checkStr(contact1.impp, contact2.impp, "Same impp");
|
||||
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");
|
||||
|
||||
for (var i in contact1.adr)
|
||||
checkAddress(contact1.adr[i], contact2.adr[i]);
|
||||
}
|
||||
|
||||
var req;
|
||||
var index = 0;
|
||||
|
||||
var mozContacts = window.navigator.mozContacts
|
||||
|
||||
var steps = [
|
||||
function () {
|
||||
ok(true, "Deleting database");
|
||||
req = mozContacts.clear();
|
||||
req.onsuccess = function () {
|
||||
ok(true, "Deleted the database");
|
||||
next();
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Retrieving all contacts");
|
||||
req = mozContacts.find({});
|
||||
req.onsuccess = function () {
|
||||
ok(req.result.length == 0, "Empty database is empty.");
|
||||
next();
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Adding empty contact");
|
||||
createResult1 = new mozContact();
|
||||
createResult1.init({});
|
||||
req = navigator.mozContacts.save(createResult1);
|
||||
req.onsuccess = function () {
|
||||
ok(createResult1.id, "The contact now has an ID.");
|
||||
sample_id1 = createResult1.id;
|
||||
next();
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Retrieving all contacts");
|
||||
req = mozContacts.find({});
|
||||
req.onsuccess = function () {
|
||||
ok(req.result.length == 1, "One contact.");
|
||||
findResult1 = req.result[0];
|
||||
next();
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Deleting empty contact");
|
||||
req = navigator.mozContacts.remove(findResult1);
|
||||
req.onsuccess = function () {
|
||||
var req2 = mozContacts.find({});
|
||||
req2.onsuccess = function () {
|
||||
ok(req2.result.length == 0, "Empty Database.");
|
||||
clearTemps();
|
||||
next();
|
||||
}
|
||||
req2.onerror = onFailure;
|
||||
}
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Adding a new contact1");
|
||||
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 all contacts");
|
||||
req = mozContacts.find({});
|
||||
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, findResult1);
|
||||
ok(findResult1.updated, "Has updated field");
|
||||
ok(findResult1.published, "Has published field");
|
||||
next();
|
||||
}
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Modifying contact1");
|
||||
findResult1.impp = properties1.impp = (["phil impp"]);
|
||||
req = navigator.mozContacts.save(findResult1);
|
||||
req.onsuccess = function () {
|
||||
var req2 = mozContacts.find({});
|
||||
req2.onsuccess = function() {
|
||||
ok(req2.result.length == 1, "Found exactly 1 contact.");
|
||||
findResult2 = req2.result[0];
|
||||
ok(findResult2.id == sample_id1, "Same ID");
|
||||
checkContacts(findResult2, properties1);
|
||||
ok(findResult2.impp.length == 1, "Found exactly 1 IMS info.");
|
||||
next();
|
||||
};
|
||||
req2.onerror = onFailure;
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function() {
|
||||
ok(true, "Saving old contact, should abort!");
|
||||
req = mozContacts.save(createResult1);
|
||||
req.onsuccess = onUnwantedSuccess;
|
||||
req.onerror = function() { ok(true, "Successfully declined updating old contact!"); next(); };
|
||||
},
|
||||
function() {
|
||||
ok(true, "Saving old contact, should abort!");
|
||||
req = mozContacts.save(findResult1)
|
||||
req.onsuccess = onUnwantedSuccess;
|
||||
req.onerror = function() { ok(true, "Successfully declined updating old contact!"); next(); };
|
||||
},
|
||||
function () {
|
||||
ok(true, "Retrieving a specific contact by ID");
|
||||
var options = {filterBy: ["id"],
|
||||
filterOp: "equals",
|
||||
filterValue: sample_id1};
|
||||
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();
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Retrieving a specific contact by givenName");
|
||||
var options = {filterBy: ["givenName"],
|
||||
filterOp: "equals",
|
||||
filterValue: properties1.givenName[0]};
|
||||
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(findResult1, createResult1);
|
||||
next();
|
||||
}
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Modifying contact2");
|
||||
findResult1.impp = properties1.impp = (["phil impp"]);
|
||||
req = mozContacts.save(findResult1);
|
||||
req.onsuccess = function () {
|
||||
var req2 = mozContacts.find({});
|
||||
req2.onsuccess = function () {
|
||||
ok(req2.result.length == 1, "Found exactly 1 contact.");
|
||||
findResult1 = req2.result[0];
|
||||
ok(findResult1.id == sample_id1, "Same ID");
|
||||
checkContacts(findResult1, createResult1);
|
||||
ok(findResult1.impp.length == 1, "Found exactly 1 IMS info.");
|
||||
next();
|
||||
}
|
||||
req2.onerror = onFailure;
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Searching contacts by query");
|
||||
var options = {filterBy: ["name", "email"],
|
||||
filterOp: "icontains",
|
||||
filterValue: properties1.name[0].substring(0,4)};
|
||||
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(findResult1, createResult1);
|
||||
next();
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Searching contacts by query");
|
||||
var options = {filterBy: ["nickname", "email"],
|
||||
filterOp: "icontains",
|
||||
filterValue: properties1.nickname};
|
||||
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(findResult1, createResult1);
|
||||
next();
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Searching contacts with multiple indices");
|
||||
var options = {filterBy: ["nickname", "email", "name"],
|
||||
filterOp: "equals",
|
||||
filterValue: properties1.nickname};
|
||||
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(findResult1, createResult1);
|
||||
next();
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Modifying contact3");
|
||||
findResult1.email = (properties1.nickname);
|
||||
findResult1.nickname = "TEST";
|
||||
var newContact = new mozContact();
|
||||
newContact.init(findResult1);
|
||||
req = mozContacts.save(newContact);
|
||||
req.onsuccess = function () {
|
||||
var options = {filterBy: ["nickname", "email", "name"],
|
||||
filterOp: "equals",
|
||||
filterValue: properties1.nickname};
|
||||
// One contact has it in nickname and the other in email
|
||||
var req2 = mozContacts.find(options);
|
||||
req2.onsuccess = function () {
|
||||
ok(req2.result.length == 2, "Found exactly 2 contacts.");
|
||||
ok(req2.result[0].id != req2.result[1].id, "Different ID");
|
||||
next();
|
||||
}
|
||||
req2.onerror = onFailure;
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Deleting contact" + findResult1.id);
|
||||
req = mozContacts.remove(findResult1);
|
||||
req.onsuccess = function () {
|
||||
var req2 = mozContacts.find({});
|
||||
req2.onsuccess = function () {
|
||||
ok(req2.result.length == 1, "One contact left.");
|
||||
findResult1 = req2.result[0];
|
||||
next();
|
||||
}
|
||||
req2.onerror = onFailure;
|
||||
}
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Deleting database");
|
||||
req = mozContacts.remove(findResult1);
|
||||
req.onsuccess = function () {
|
||||
clearTemps();
|
||||
next();
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Adding a new contact");
|
||||
createResult1 = new mozContact();
|
||||
createResult1.init(properties1);
|
||||
req = mozContacts.save(createResult1)
|
||||
req.onsuccess = function () {
|
||||
ok(createResult1.id, "The contact now has an ID.");
|
||||
sample_id1 = createResult1.id;
|
||||
next();
|
||||
}
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Adding a new contact2");
|
||||
createResult2 = new mozContact();
|
||||
createResult2.init(properties2);
|
||||
req = mozContacts.save(createResult2);
|
||||
req.onsuccess = function () {
|
||||
ok(createResult2.id, "The contact now has an ID.");
|
||||
sample_id2 = createResult2.id;
|
||||
next();
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Retrieving all contacts");
|
||||
req = mozContacts.find({})
|
||||
req.onsuccess = function () {
|
||||
ok(req.result.length == 2, "Found exactly 2 contact.");
|
||||
next();
|
||||
}
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
console.log("Searching contacts by query1");
|
||||
var options = {filterBy: ["name", "email"],
|
||||
filterOp: "icontains",
|
||||
filterValue: properties1.name[0].substring(0, 4)}
|
||||
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(findResult1, createResult1);
|
||||
next();
|
||||
}
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Searching contacts by query2");
|
||||
var options = {filterBy: ["name", "email"],
|
||||
filterOp: "icontains",
|
||||
filterValue: properties2.name[0].substring(0, 4)};
|
||||
req = mozContacts.find(options);
|
||||
req.onsuccess = function () {
|
||||
ok(req.result.length == 1, "Found exactly 1 contact.");
|
||||
findResult1 = req.result[0];
|
||||
ok(findResult1.adr.length == 2, "Adr length 2");
|
||||
checkContacts(findResult1, createResult2);
|
||||
next();
|
||||
}
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Searching contacts by tel");
|
||||
var options = {filterBy: ["tel"],
|
||||
filterOp: "contains",
|
||||
filterValue: properties2.tel[0].substring(0, 7)};
|
||||
req = mozContacts.find(options);
|
||||
req.onsuccess = function () {
|
||||
ok(req.result.length == 1, "Found exactly 1 contact.");
|
||||
findResult1 = req.result[0];
|
||||
ok(findResult1.id == sample_id2, "Same ID");
|
||||
checkContacts(findResult1, createResult2);
|
||||
next();
|
||||
}
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Searching contacts by email");
|
||||
var options = {filterBy: ["email"],
|
||||
filterOp: "contains",
|
||||
filterValue: properties2.email[0].substring(0, 4)};
|
||||
req = mozContacts.find(options);
|
||||
req.onsuccess = function () {
|
||||
ok(req.result.length == 1, "Found exactly 1 contact.");
|
||||
findResult1 = req.result[0];
|
||||
ok(findResult1.id == sample_id2, "Same ID");
|
||||
checkContacts(findResult1, createResult2);
|
||||
next();
|
||||
}
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Deleting database");
|
||||
req = mozContacts.clear();
|
||||
req.onsuccess = function () {
|
||||
clearTemps();
|
||||
next();
|
||||
}
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Adding 100 contacts");
|
||||
for (var i=0; i<99; i++) {
|
||||
createResult1 = new mozContact();
|
||||
createResult1.init(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);
|
||||
req = mozContacts.save(createResult1);
|
||||
req.onsuccess = function () {
|
||||
ok(createResult1.id, "The contact now has an ID.");
|
||||
ok(createResult1.name == properties1.name, "Same Name");
|
||||
next();
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Retrieving all contacts");
|
||||
req = mozContacts.find({});
|
||||
req.onsuccess = function () {
|
||||
ok(req.result.length == 100, "100 Entries.");
|
||||
next();
|
||||
}
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Retrieving all contacts with limit 10");
|
||||
var options = { filterLimit: 10 };
|
||||
req = mozContacts.find(options);
|
||||
req.onsuccess = function () {
|
||||
ok(req.result.length == 10, "10 Entries.");
|
||||
next();
|
||||
}
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Retrieving all contacts2");
|
||||
var options = {filterBy: ["name"],
|
||||
filterOp: "icontains",
|
||||
filterValue: properties2.name[0].substring(0, 4)};
|
||||
req = mozContacts.find({});
|
||||
req.onsuccess = function () {
|
||||
ok(req.result.length == 100, "100 Entries.");
|
||||
checkContacts(createResult1, req.result[99]);
|
||||
next();
|
||||
}
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Deleting database");
|
||||
req = mozContacts.clear();
|
||||
req.onsuccess = function () {
|
||||
clearTemps();
|
||||
next();
|
||||
}
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Testing clone contact");
|
||||
createResult1 = new mozContact();
|
||||
createResult1.init(properties1);
|
||||
req = mozContacts.save(createResult1);
|
||||
req.onsuccess = function () {
|
||||
ok(createResult1.id, "The contact now has an ID.");
|
||||
ok(createResult1.name == properties1.name, "Same Name");
|
||||
next();
|
||||
}
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function() {
|
||||
ok(true, "Testing clone contact2");
|
||||
var cloned = new mozContact(createResult1);
|
||||
ok(cloned.id != createResult1.id, "Cloned contact has new ID");
|
||||
cloned.email = "new email!";
|
||||
cloned.givenName = "Tom";
|
||||
req = mozContacts.save(cloned);
|
||||
req.onsuccess = function () {
|
||||
ok(cloned.id, "The contact now has an ID.");
|
||||
ok(cloned.email == "new email!", "Same Email");
|
||||
ok(createResult1.email != cloned.email, "Clone has different email");
|
||||
ok(cloned.givenName == "Tom", "New Name");
|
||||
next();
|
||||
}
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Retrieving all contacts");
|
||||
var options = {filterBy: ["name"],
|
||||
filterOp: "icontains",
|
||||
filterValue: properties2.name[0].substring(0, 4)};
|
||||
req = mozContacts.find({});
|
||||
req.onsuccess = function () {
|
||||
ok(req.result.length == 2, "2 Entries.");
|
||||
next();
|
||||
}
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Search with redundant fields should only return 1 contact");
|
||||
createResult1 = new mozContact();
|
||||
createResult1.init({name: "XXX", nickname: "XXX", email: "XXX", tel: "XXX"});
|
||||
req = mozContacts.save(createResult1);
|
||||
req.onsuccess = function() {
|
||||
var options = {filterBy: [],
|
||||
filterOp: "equals",
|
||||
filterValue: "XXX"};
|
||||
var req2 = mozContacts.find(options);
|
||||
req2.onsuccess = function() {
|
||||
ok(req2.result.length == 1, "1 Entry");
|
||||
next();
|
||||
}
|
||||
req2.onerror = onFailure;
|
||||
}
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "Deleting database");
|
||||
req = mozContacts.clear()
|
||||
req.onsuccess = function () {
|
||||
ok(true, "Deleted the database");
|
||||
next();
|
||||
}
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
function () {
|
||||
ok(true, "all done!\n");
|
||||
clearTemps();
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
];
|
||||
|
||||
function next() {
|
||||
ok(true, "Begin!");
|
||||
if (index >= steps.length) {
|
||||
ok(false, "Shouldn't get here!");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
steps[index]();
|
||||
} catch(ex) {
|
||||
ok(false, "Caught exception", ex);
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
|
||||
function permissionTest() {
|
||||
if (gContactsEnabled) {
|
||||
next();
|
||||
} else {
|
||||
is(mozContacts, null, "mozContacts is null when not enabled.");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
var gContactsEnabled = SpecialPowers.getBoolPref("dom.mozContacts.enabled");
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(permissionTest);
|
||||
|
||||
ok(true, "test passed");
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -4,6 +4,7 @@ DOM_SRCDIRS = \
|
|||
dom/power \
|
||||
dom/network/src \
|
||||
dom/sms/src \
|
||||
dom/contacts \
|
||||
dom/src/events \
|
||||
dom/src/storage \
|
||||
dom/src/offline \
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# 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/.
|
||||
|
||||
DEPTH = ../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MODULE = dom
|
||||
XPIDL_MODULE = dom_contacts
|
||||
GRE_MODULE = 1
|
||||
|
||||
XPIDLSRCS = \
|
||||
nsIDOMContactProperties.idl \
|
||||
nsIDOMContactManager.idl \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
|
@ -0,0 +1,33 @@
|
|||
/* 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"
|
||||
|
||||
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)]
|
||||
interface nsIDOMContactManager : nsISupports
|
||||
{
|
||||
nsIDOMDOMRequest find(in nsIDOMContactFindOptions options);
|
||||
|
||||
nsIDOMDOMRequest clear();
|
||||
|
||||
nsIDOMDOMRequest save(in nsIDOMContact contact);
|
||||
|
||||
nsIDOMDOMRequest remove(in nsIDOMContact contact);
|
||||
};
|
|
@ -0,0 +1,52 @@
|
|||
/* 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"
|
||||
|
||||
interface nsIArray;
|
||||
interface nsIDOMContact;
|
||||
|
||||
[scriptable, uuid(27a568b0-cee1-11e0-9572-0800200c9a66)]
|
||||
interface nsIDOMContactAddress : nsISupports
|
||||
{
|
||||
attribute DOMString streetAddress;
|
||||
attribute DOMString locality;
|
||||
attribute DOMString region;
|
||||
attribute DOMString postalCode;
|
||||
attribute DOMString countryName;
|
||||
};
|
||||
|
||||
[scriptable, uuid(e31daea0-0cb6-11e1-be50-0800200c9a66)]
|
||||
interface nsIDOMContactFindOptions : nsISupports
|
||||
{
|
||||
attribute DOMString filterValue; // e.g. "Tom"
|
||||
attribute DOMString filterOp; // e.g. "contains"
|
||||
attribute jsval filterBy; // DOMString[], e.g. ["givenName", "nickname"]
|
||||
attribute unsigned long filterLimit;
|
||||
};
|
||||
|
||||
[scriptable, uuid(53ed7c20-ceda-11e0-9572-0800200c9a66)]
|
||||
interface nsIDOMContactProperties : 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; // DOMString[]
|
||||
attribute jsval photo; // DOMString[]
|
||||
attribute jsval url; // DOMString[]
|
||||
attribute jsval category; // DOMString[]
|
||||
attribute jsval adr; // ContactAddress[]
|
||||
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
|
||||
};
|
|
@ -93,6 +93,7 @@ SHARED_LIBRARY_LIBS = \
|
|||
$(DEPTH)/view/src/$(LIB_PREFIX)gkview_s.$(LIB_SUFFIX) \
|
||||
$(DEPTH)/dom/base/$(LIB_PREFIX)jsdombase_s.$(LIB_SUFFIX) \
|
||||
$(DEPTH)/dom/battery/$(LIB_PREFIX)dom_battery_s.$(LIB_SUFFIX) \
|
||||
$(DEPTH)/dom/contacts/$(LIB_PREFIX)jsdomcontacts_s.$(LIB_SUFFIX) \
|
||||
$(DEPTH)/dom/power/$(LIB_PREFIX)dom_power_s.$(LIB_SUFFIX) \
|
||||
$(DEPTH)/dom/network/src/$(LIB_PREFIX)dom_network_s.$(LIB_SUFFIX) \
|
||||
$(DEPTH)/dom/sms/src/$(LIB_PREFIX)dom_sms_s.$(LIB_SUFFIX) \
|
||||
|
@ -258,6 +259,7 @@ LOCAL_INCLUDES += -I$(srcdir)/../base \
|
|||
-I$(topsrcdir)/dom/src/storage \
|
||||
-I$(topsrcdir)/dom/src/offline \
|
||||
-I$(topsrcdir)/dom/src/geolocation \
|
||||
-I$(topsrcdir)/dom/contacts \
|
||||
-I$(topsrcdir)/dom/telephony \
|
||||
-I. \
|
||||
-I$(topsrcdir)/editor/libeditor/base \
|
||||
|
|
|
@ -3439,6 +3439,10 @@ pref("dom.battery.enabled", true);
|
|||
pref("dom.sms.enabled", false);
|
||||
pref("dom.sms.whitelist", "");
|
||||
|
||||
// WebContacts
|
||||
pref("dom.mozContacts.enabled", false);
|
||||
pref("dom.mozContacts.whitelist", "");
|
||||
|
||||
// enable JS dump() function.
|
||||
pref("browser.dom.window.dump.enabled", false);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче