diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in index 558a15c8b53..c45f190f531 100644 --- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -471,6 +471,8 @@ @BINPATH@/components/DOMWifiManager.manifest @BINPATH@/components/NetworkStatsManager.js @BINPATH@/components/NetworkStatsManager.manifest +@BINPATH@/components/NetworkInterfaceListService.manifest +@BINPATH@/components/NetworkInterfaceListService.js #endif #ifdef MOZ_B2G_FM @BINPATH@/components/DOMFMRadioChild.js diff --git a/dom/bluetooth/linux/BluetoothDBusService.cpp b/dom/bluetooth/linux/BluetoothDBusService.cpp index 8a326f16c27..6cf12c42d8b 100644 --- a/dom/bluetooth/linux/BluetoothDBusService.cpp +++ b/dom/bluetooth/linux/BluetoothDBusService.cpp @@ -903,14 +903,7 @@ RunDBusCallback(DBusMessage* aMsg, void* aBluetoothReplyRunnable, // being gtk based, sometimes we'll get signals/reply coming in on the main // thread. There's not a lot we can do about that for the time being and it // (technically) shouldn't hurt anything. However, on gonk, die. - - // Due to the fact introducing workaround in Bug 827888, the callback for a - // message gets executed immediately. The proper fix is in bug 830290, but - // it's a intrusive change, it is better to remove assertion here since it - // would not hurt anything. - // Tracking bug 830290 for intrusive solution. - - // MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(!NS_IsMainThread()); #endif nsRefPtr replyRunnable = dont_AddRef(static_cast< BluetoothReplyRunnable* >(aBluetoothReplyRunnable)); diff --git a/dom/contacts/ContactManager.js b/dom/contacts/ContactManager.js index c87302cc874..e28acc7a340 100644 --- a/dom/contacts/ContactManager.js +++ b/dom/contacts/ContactManager.js @@ -205,6 +205,39 @@ 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; + } + if (!(aBlob[i] instanceof Components.interfaces.nsIDOMBlob)) { + return null; + } + } + return aBlob; + } + return null; +} + +function isVanillaObj(aObj) { + return Object.prototype.toString.call(aObj) == "[object Object]"; +} + +function validateArrayField(data, createCb) { + if (data) { + data = Array.isArray(data) ? data : [data]; + let filtered = []; + for (let obj of data) { + if (obj && isVanillaObj(obj)) { + filtered.push(createCb(obj)); + } + } + return filtered; + } + return undefined; +} + function Contact() { }; Contact.prototype = { @@ -235,120 +268,220 @@ Contact.prototype = { genderIdentity: '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; + }, + + set email(aEmail) { + this._email = validateArrayField(aEmail, function(email) { + return new ContactField(email.type, email.value, email.pref); + }); + }, + + get email() { + return this._email; + }, + + 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); + }); + }, + + get adr() { + return this._adr; + }, + + set tel(aTel) { + this._tel = validateArrayField(aTel, function(tel) { + return new ContactTelField(tel.type, tel.value, tel.carrier, tel.pref); + }); + }, + + get tel() { + return this._tel; + }, + + set impp(aImpp) { + this._impp = validateArrayField(aImpp, function(impp) { + return new ContactField(impp.type, impp.value, impp.pref); + }); + }, + + get impp() { + return this._impp; + }, + + set url(aUrl) { + this._url = validateArrayField(aUrl, function(url) { + return new ContactField(url.type, url.value, url.pref); + }); + }, + + get url() { + return this._url; + }, + + set org(aOrg) { + this._org = sanitizeStringArray(aOrg); + }, + + 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 !== undefined && aBday !== null) { + this._bday = new Date(aBday); + } + }, + + get bday() { + return this._bday; + }, + + set anniversary(aAnniversary) { + if (aAnniversary !== undefined && aAnniversary !== null) { + 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; + }, + init: function init(aProp) { - function _checkBlobArray(aBlob) { - if (Array.isArray(aBlob)) { - for (let i = 0; i < aBlob.length; i++) { - if (typeof aBlob != 'object') { - return null; - } - if (!(aBlob[i] instanceof Components.interfaces.nsIDOMBlob)) { - return null; - } - } - return aBlob; - } - return null; - } - - function _isVanillaObj(aObj) { - return Object.prototype.toString.call(aObj) == "[object Object]"; - } - - let _create = sanitizeStringArray; - - this.name = _create(aProp.name); - this.honorificPrefix = _create(aProp.honorificPrefix); - this.givenName = _create(aProp.givenName); - this.additionalName = _create(aProp.additionalName); - this.familyName = _create(aProp.familyName); - this.honorificSuffix = _create(aProp.honorificSuffix); - this.nickname = _create(aProp.nickname); - - if (aProp.email) { - aProp.email = Array.isArray(aProp.email) ? aProp.email : [aProp.email]; - this.email = new Array(); - for (let email of aProp.email) { - if (_isVanillaObj(email)) { - this.email.push(new ContactField(email.type, email.value, email.pref)); - } else if (DEBUG) { - debug("email field is not a ContactField and was ignored."); - } - } - } else if (DEBUG) { - this.email = null; - } - - this.photo = _checkBlobArray(aProp.photo); - this.category = _create(aProp.category); - - if (aProp.adr) { - aProp.adr = Array.isArray(aProp.adr) ? aProp.adr : [aProp.adr]; - this.adr = new Array(); - for (let adr of aProp.adr) { - if (_isVanillaObj(adr)) { - this.adr.push(new ContactAddress(adr.type, adr.streetAddress, adr.locality, - adr.region, adr.postalCode, adr.countryName, - adr.pref)); - } else if (DEBUG) { - debug("adr field is not a ContactAddress and was ignored."); - } - } - } else { - this.adr = null; - } - - if (aProp.tel) { - aProp.tel = Array.isArray(aProp.tel) ? aProp.tel : [aProp.tel]; - this.tel = new Array(); - for (let tel of aProp.tel) { - if (_isVanillaObj(tel)) { - this.tel.push(new ContactTelField(tel.type, tel.value, tel.carrier, - tel.pref)); - } else if (DEBUG) { - debug("tel field is not a ContactTelField and was ignored."); - } - } - } else { - this.tel = null; - } - - this.org = _create(aProp.org); - this.jobTitle = _create(aProp.jobTitle); - this.bday = (aProp.bday == undefined || aProp.bday == null) ? null : new Date(aProp.bday); - this.note = _create(aProp.note); - - if (aProp.impp) { - aProp.impp = Array.isArray(aProp.impp) ? aProp.impp : [aProp.impp]; - this.impp = new Array(); - for (let impp of aProp.impp) { - if (_isVanillaObj(impp)) { - this.impp.push(new ContactField(impp.type, impp.value, impp.pref)); - } else if (DEBUG) { - debug("impp field is not a ContactField and was ignored."); - } - } - } else { - this.impp = null; - } - - if (aProp.url) { - aProp.url = Array.isArray(aProp.url) ? aProp.url : [aProp.url]; - this.url = new Array(); - for (let url of aProp.url) { - if (_isVanillaObj(url)) { - this.url.push(new ContactField(url.type, url.value, url.pref)); - } else if (DEBUG) { - debug("url field is not a ContactField and was ignored."); - } - } - } else { - this.url = 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; + this.name = aProp.name; + this.honorificPrefix = aProp.honorificPrefix; + this.givenName = aProp.givenName; + this.additionalName = aProp.additionalName; + this.familyName = aProp.familyName; + this.honorificSuffix = aProp.honorificSuffix; + this.nickname = aProp.nickname; + this.email = aProp.email; + this.photo = aProp.photo; + this.url = aProp.url; + this.category = aProp.category; + this.adr = aProp.adr; + this.tel = aProp.tel; + this.org = aProp.org; + this.jobTitle = aProp.jobTitle; + this.bday = aProp.bday; + this.note = aProp.note; + this.impp = aProp.impp; + this.anniversary = aProp.anniversary; + this.sex = aProp.sex; + this.genderIdentity = aProp.genderIdentity; }, get published () { @@ -380,7 +513,7 @@ Contact.prototype = { // ContactManager const CONTACTMANAGER_CONTRACTID = "@mozilla.org/contactManager;1"; -const CONTACTMANAGER_CID = Components.ID("{7bfb6481-f946-4254-afc5-d7fe9f5c45a3}"); +const CONTACTMANAGER_CID = Components.ID("{8beb3a66-d70a-4111-b216-b8e995ad3aff}"); const nsIDOMContactManager = Components.interfaces.nsIDOMContactManager; function ContactManager() @@ -525,6 +658,13 @@ ContactManager.prototype = { Services.DOMRequest.fireSuccess(req, msg.revision); } break; + case "Contacts:Count": + if (DEBUG) debug("count: " + msg.count); + req = this.getRequest(msg.requestID); + if (req) { + Services.DOMRequest.fireSuccess(req, msg.count); + } + break; default: if (DEBUG) debug("Wrong message: " + aMessage.name); } @@ -545,6 +685,7 @@ ContactManager.prototype = { case "find": case "listen": case "revision": + case "count": access = "read"; break; default: @@ -734,6 +875,23 @@ ContactManager.prototype = { return request; }, + getCount: function() { + let request = this.createRequest(); + + let allowCallback = function() { + cpmm.sendAsyncMessage("Contacts:GetCount", { + requestID: this.getRequestId(request) + }); + }.bind(this); + + let cancelCallback = function() { + Services.DOMRequest.fireError(request); + }; + + this.askPermission("count", request, allowCallback, cancelCallback); + return request; + }, + init: function(aWindow) { this.initHelper(aWindow, ["Contacts:Find:Return:OK", "Contacts:Find:Return:KO", "Contacts:Clear:Return:OK", "Contacts:Clear:Return:KO", @@ -741,8 +899,8 @@ ContactManager.prototype = { "Contact:Remove:Return:OK", "Contact:Remove:Return:KO", "Contact:Changed", "PermissionPromptHelper:AskPermission:OK", - "Contacts:GetAll:Next", - "Contacts:Revision"]); + "Contacts:GetAll:Next", "Contacts:Revision", + "Contacts:Count"]); }, // Called from DOMRequestIpcHelper diff --git a/dom/contacts/ContactManager.manifest b/dom/contacts/ContactManager.manifest index d7e3270f196..0b34c5833f6 100644 --- a/dom/contacts/ContactManager.manifest +++ b/dom/contacts/ContactManager.manifest @@ -20,6 +20,6 @@ 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 {7bfb6481-f946-4254-afc5-d7fe9f5c45a3} ContactManager.js -contract @mozilla.org/contactManager;1 {7bfb6481-f946-4254-afc5-d7fe9f5c45a3} +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 diff --git a/dom/contacts/fallback/ContactDB.jsm b/dom/contacts/fallback/ContactDB.jsm index 00a23a94397..605c3905178 100644 --- a/dom/contacts/fallback/ContactDB.jsm +++ b/dom/contacts/fallback/ContactDB.jsm @@ -727,7 +727,16 @@ ContactDB.prototype = { this.newTxn("readonly", REVISION_STORE, function (txn, store) { store.get(REVISION_KEY).onsuccess = function (e) { aSuccessCb(e.target.result); - } + }; + }); + }, + + getCount: function CDB_getCount(aSuccessCb) { + if (DEBUG) debug("getCount"); + this.newTxn("readonly", STORE_NAME, function (txn, store) { + store.count().onsuccess = function (e) { + aSuccessCb(e.target.result); + }; }); }, diff --git a/dom/contacts/fallback/ContactService.jsm b/dom/contacts/fallback/ContactService.jsm index 74808338ac2..b44f81fdedc 100644 --- a/dom/contacts/fallback/ContactService.jsm +++ b/dom/contacts/fallback/ContactService.jsm @@ -29,7 +29,8 @@ let ContactService = { this._messages = ["Contacts:Find", "Contacts:GetAll", "Contacts:GetAll:SendNow", "Contacts:Clear", "Contact:Save", "Contact:Remove", "Contacts:RegisterForMessages", - "child-process-shutdown", "Contacts:GetRevision"]; + "child-process-shutdown", "Contacts:GetRevision", + "Contacts:GetCount"]; this._children = []; this._cursors = {}; this._messages.forEach(function(msgName) { @@ -184,6 +185,19 @@ let ContactService = { } ); break; + case "Contacts:GetCount": + if (!this.assertPermission(aMessage, "contacts-read")) { + return null; + } + this._db.getCount( + function(count) { + mm.sendAsyncMessage("Contacts:Count", { + requestID: msg.requestID, + count: count + }); + } + ); + break; case "Contacts:RegisterForMessages": if (!aMessage.target.assertPermission("contacts-read")) { return null; diff --git a/dom/contacts/tests/test_contacts_basics.html b/dom/contacts/tests/test_contacts_basics.html index a8e18559427..fb576fc41f3 100644 --- a/dom/contacts/tests/test_contacts_basics.html +++ b/dom/contacts/tests/test_contacts_basics.html @@ -137,6 +137,14 @@ function onFailure() { } function checkStr(str1, str2, msg) { + if (str1 ^ str2) { + ok(false, "Expected both strings to be either present or absent"); + return; + } + is(str1, str2, msg); +} + +function checkStrArray(str1, str2, msg) { // comparing /[null(,null)+]/ and undefined should pass function nonNull(e) { return e != null; @@ -150,88 +158,71 @@ function checkStr(str1, str2, msg) { } 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"); + if (adr1 ^ adr2) { + ok(false, "Expected both adrs to be either present or absent"); + return; + } + dump("adr1: " + adr1 + ", adr2: " + adr2 + "\n"); + 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"); is(adr1.pref, adr2.pref, "Same pref"); } 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 (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"); is(tel1.pref, tel2.pref, "Same 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"); is(field1.pref, field2.pref, "Same pref"); } 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"); + 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"); + checkStrArray(contact1.category, contact2.category, "Same 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"); for (var 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 (var 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 (var 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 (var 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 (var 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]); } } @@ -250,6 +241,15 @@ function checkRevision(revision, msg, then) { revReq.onerror = onFailure; } +function checkCount(count, msg, then) { + var request = navigator.mozContacts.getCount(); + request.onsuccess = function(e) { + is(e.target.result, count, msg); + then(); + }; + request.onerror = onFailure; +} + var mozContacts = window.navigator.mozContacts; ok(mozContacts, "mozContacts exists"); ok("mozContact" in window, "mozContact exists"); @@ -266,7 +266,9 @@ var steps = [ req = mozContacts.clear(); req.onsuccess = function () { ok(true, "Deleted the database"); - checkRevision(1, "Revision was incremented on clear", next); + checkCount(0, "No contacts after clear", function() { + checkRevision(1, "Revision was incremented on clear", next); + }); }; req.onerror = onFailure; }); @@ -288,7 +290,9 @@ var steps = [ req.onsuccess = function () { ok(createResult1.id, "The contact now has an ID."); sample_id1 = createResult1.id; - checkRevision(2, "Revision was incremented on save", next); + checkCount(1, "1 contact after adding empty contact", function() { + checkRevision(2, "Revision was incremented on save", next); + }); }; req.onerror = onFailure; }, @@ -331,7 +335,7 @@ var steps = [ req.onsuccess = function () { ok(createResult1.id, "The contact now has an ID."); sample_id1 = createResult1.id; - checkContacts(properties1, createResult1); + checkContacts(createResult1, properties1); }; req.onerror = onFailure; }, @@ -498,7 +502,7 @@ var steps = [ req.onsuccess = function () { ok(createResult1.id, "The contact now has an ID."); sample_id1 = createResult1.id; - checkContacts(properties1, createResult1); + checkContacts(createResult1, properties1); next(); }; req.onerror = onFailure; @@ -999,7 +1003,7 @@ var steps = [ req.onsuccess = function () { ok(createResult1.id, "The contact now has an ID."); ok(createResult1.name == properties1.name, "Same Name"); - next(); + checkCount(20, "20 contacts in DB", next); }; req.onerror = onFailure; }, @@ -1091,7 +1095,7 @@ var steps = [ req = mozContacts.save(cloned); req.onsuccess = function () { ok(cloned.id, "The contact now has an ID."); - ok(cloned.email.value == "new email!", "Same Email"); + ok(cloned.email[0].value == "new email!", "Same Email"); ok(createResult1.email != cloned.email, "Clone has different email"); ok(cloned.givenName == "Tom", "New Name"); next(); @@ -1255,7 +1259,7 @@ var steps = [ var req2 = mozContacts.find(options); req2.onsuccess = function() { is(req2.result.length, 1, "1 Entry"); - checkStr(req2.result.givenName, "customTest", "same name"); + checkStrArray(req2.result.givenName, "customTest", "same name"); ok(req2.result.yyy === undefined, "custom property undefined"); next(); } @@ -1430,6 +1434,23 @@ var steps = [ } req.onerror = onFailure; }, + 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"}; + ok(Array.isArray(createResult1[prop]), prop + " is array"); + } + next(); + }, function () { ok(true, "all done!\n"); clearTemps(); @@ -1445,11 +1466,11 @@ function next() { return; } try { - steps[index](); + var i = index++; + steps[i](); } catch(ex) { ok(false, "Caught exception", ex); } - index += 1; } SimpleTest.waitForExplicitFinish(); diff --git a/dom/interfaces/contacts/nsIDOMContactManager.idl b/dom/interfaces/contacts/nsIDOMContactManager.idl index 7e459dacaca..5ae8a3d3162 100644 --- a/dom/interfaces/contacts/nsIDOMContactManager.idl +++ b/dom/interfaces/contacts/nsIDOMContactManager.idl @@ -20,7 +20,7 @@ interface nsIDOMContact : nsIContactProperties void init(in nsIContactProperties properties); // Workaround BUG 723206 }; -[scriptable, uuid(e01ebfe7-e972-4e01-b04b-1d162dc74983)] +[scriptable, uuid(8beb3a66-d70a-4111-b216-b8e995ad3aff)] interface nsIDOMContactManager : nsISupports { nsIDOMDOMRequest find(in nsIContactFindOptions options); @@ -36,4 +36,6 @@ interface nsIDOMContactManager : nsISupports attribute nsIDOMEventListener oncontactchange; nsIDOMDOMRequest getRevision(); + + nsIDOMDOMRequest getCount(); }; diff --git a/dom/mobilemessage/src/ril/MmsService.js b/dom/mobilemessage/src/ril/MmsService.js index 3d51f8c2a2c..82702021468 100644 --- a/dom/mobilemessage/src/ril/MmsService.js +++ b/dom/mobilemessage/src/ril/MmsService.js @@ -1263,7 +1263,8 @@ MmsService.prototype = { this.retrieveMessage(url, this.retrieveMessageCallback.bind(this, wish, - savableMessage)); + savableMessage), + domMessage); }, /** @@ -1294,8 +1295,7 @@ MmsService.prototype = { .saveReceivedMessage(savableMessage, this.saveReceivedMessageCallback.bind(this, retrievalMode, - savableMessage), - domMessage); + savableMessage)); }).bind(this)); }, @@ -1476,7 +1476,7 @@ MmsService.prototype = { retrieve: function retrieve(aMessageId, aRequest) { if (DEBUG) debug("Retrieving message with ID " + aMessageId); gMobileMessageDatabaseService.getMessageRecordById(aMessageId, - (function notifyResult(aRv, aMessageRecord) { + (function notifyResult(aRv, aMessageRecord, aDomMessage) { if (Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR != aRv) { if (DEBUG) debug("Function getMessageRecordById() return error."); aRequest.notifyGetMessageFailed(aRv); @@ -1526,10 +1526,17 @@ MmsService.prototype = { let wish = aMessageRecord.headers["x-mms-delivery-report"]; let responseNotify = function responseNotify(mmsStatus, retrievedMsg) { // If the mmsStatus is still MMS_PDU_STATUS_DEFERRED after retry, - // we should not store it into database. + // we should not store it into database and update its delivery + // status to 'error'. if (MMS.MMS_PDU_STATUS_RETRIEVED !== mmsStatus) { if (DEBUG) debug("RetrieveMessage fail after retry."); - aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR); + gMobileMessageDatabaseService.setMessageDelivery(aMessageId, + null, + null, + DELIVERY_STATUS_ERROR, + function () { + aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR); + }); return; } // In OMA-TS-MMS_ENC-V1_3, Table 5 in page 25. This header field @@ -1585,7 +1592,9 @@ MmsService.prototype = { null, null, DELIVERY_STATUS_PENDING, - this.retrieveMessage(url, responseNotify.bind(this))); + this.retrieveMessage(url, + responseNotify.bind(this), + aDomMessage)); }).bind(this)); }, diff --git a/dom/system/gonk/Makefile.in b/dom/system/gonk/Makefile.in index 60b3874ed5e..a48c12ed943 100644 --- a/dom/system/gonk/Makefile.in +++ b/dom/system/gonk/Makefile.in @@ -43,6 +43,8 @@ EXTRA_COMPONENTS = \ RadioInterfaceLayer.manifest \ RadioInterfaceLayer.js \ RILContentHelper.js \ + NetworkInterfaceListService.manifest \ + NetworkInterfaceListService.js \ $(NULL) EXTRA_JS_MODULES = \ diff --git a/dom/system/gonk/NetworkInterfaceListService.js b/dom/system/gonk/NetworkInterfaceListService.js new file mode 100644 index 00000000000..2230d88db3b --- /dev/null +++ b/dom/system/gonk/NetworkInterfaceListService.js @@ -0,0 +1,63 @@ +/* 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 {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +const NETWORKLISTSERVICE_CONTRACTID = + "@mozilla.org/network/interface-list-service;1"; +const NETWORKLISTSERVICE_CID = + Components.ID("{3780be6e-7012-4e53-ade6-15212fb88a0d}"); + +XPCOMUtils.defineLazyServiceGetter(this, "cpmm", + "@mozilla.org/childprocessmessagemanager;1", + "nsISyncMessageSender"); + +function NetworkInterfaceListService () { +} + +NetworkInterfaceListService.prototype = { + classID: NETWORKLISTSERVICE_CID, + + QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInterfaceListService]), + + getDataInterfaceList: function(aConditions) { + return new NetworkInterfaceList( + cpmm.sendSyncMessage( + 'NetworkInterfaceList:ListInterface', + { + excludeSupl: (aConditions & + Ci.nsINetworkInterfaceListService. + LIST_NOT_INCLUDE_SUPL_INTERFACES) != 0, + excludeMms: (aConditions & + Ci.nsINetworkInterfaceListService. + LIST_NOT_INCLUDE_MMS_INTERFACES) != 0 + } + )[0]); + } +}; + +function NetworkInterfaceList (aInterfaces) { + this._interfaces = aInterfaces; +} + +NetworkInterfaceList.prototype = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInterfaceList]), + getNumberOfInterface: function() { + return this._interfaces.length; + }, + + getInterface: function(index) { + if (!this._interfaces) { + return null; + } + return this._interfaces[index]; + } +}; + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkInterfaceListService]); + diff --git a/dom/system/gonk/NetworkInterfaceListService.manifest b/dom/system/gonk/NetworkInterfaceListService.manifest new file mode 100644 index 00000000000..a827e778f5e --- /dev/null +++ b/dom/system/gonk/NetworkInterfaceListService.manifest @@ -0,0 +1,17 @@ +# Copyright 2012 Mozilla Foundation and Mozilla contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# NetworkInterfaceListService.js +component {3780be6e-7012-4e53-ade6-15212fb88a0d} NetworkInterfaceListService.js +contract @mozilla.org/network/interface-list-service;1 {3780be6e-7012-4e53-ade6-15212fb88a0d} diff --git a/dom/system/gonk/NetworkManager.js b/dom/system/gonk/NetworkManager.js index 5c5006674f9..3affc853603 100644 --- a/dom/system/gonk/NetworkManager.js +++ b/dom/system/gonk/NetworkManager.js @@ -22,6 +22,10 @@ const DEFAULT_PREFERRED_NETWORK_TYPE = Ci.nsINetworkInterface.NETWORK_TYPE_WIFI; XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService", "@mozilla.org/settingsService;1", "nsISettingsService"); +XPCOMUtils.defineLazyGetter(this, "ppmm", function() { + return Cc["@mozilla.org/parentprocessmessagemanager;1"] + .getService(Ci.nsIMessageBroadcaster); +}); XPCOMUtils.defineLazyServiceGetter(this, "gDNSService", "@mozilla.org/network/dns-service;1", @@ -210,6 +214,8 @@ function NetworkManager() { debug("Error reading the 'tethering.wifi.enabled' setting: " + aErrorMessage); } }); + + ppmm.addMessageListener('NetworkInterfaceList:ListInterface', this); } NetworkManager.prototype = { classID: NETWORKMANAGER_CID, @@ -320,6 +326,38 @@ NetworkManager.prototype = { } }, + receiveMessage: function receiveMessage(aMsg) { + switch (aMsg.name) { + case "NetworkInterfaceList:ListInterface": { + let excludeMms = aMsg.json.exculdeMms; + let excludeSupl = aMsg.json.exculdeSupl; + let interfaces = []; + + for each (let i in this.networkInterfaces) { + if ((i.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS && excludeMms) || + (i.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_SUPL && excludeSupl)) { + continue; + } + interfaces.push({ + state: i.state, + type: i.type, + name: i.name, + dhcp: i.dhcp, + ip: i.ip, + netmask: i.netmask, + broadcast: i.broadcast, + gateway: i.gateway, + dns1: i.dns1, + dns2: i.dns2, + httpProxyHost: i.httpProxyHost, + httpProxyPort: i.httpProxyPort + }); + } + return interfaces; + } + } + }, + // nsINetworkManager registerNetworkInterface: function registerNetworkInterface(network) { diff --git a/dom/system/gonk/moz.build b/dom/system/gonk/moz.build index 4caea5b7649..3b82b798650 100644 --- a/dom/system/gonk/moz.build +++ b/dom/system/gonk/moz.build @@ -17,6 +17,7 @@ XPIDL_SOURCES += [ 'nsIAudioManager.idl', 'nsINavigatorAudioChannelManager.idl', + 'nsINetworkInterfaceListService.idl', 'nsINetworkManager.idl', 'nsIRadioInterfaceLayer.idl', 'nsISystemWorkerManager.idl', diff --git a/dom/system/gonk/nsINetworkInterfaceListService.idl b/dom/system/gonk/nsINetworkInterfaceListService.idl new file mode 100644 index 00000000000..9325e4aaee0 --- /dev/null +++ b/dom/system/gonk/nsINetworkInterfaceListService.idl @@ -0,0 +1,36 @@ +/* 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 "nsINetworkManager.idl" +#include "nsISupports.idl" + +[scriptable, uuid(b44d74db-c9d6-41dd-98ae-a56918d6e6ad)] +interface nsINetworkInterfaceList : nsISupports +{ + /** + * Number of the network interfaces that is available. + */ + long getNumberOfInterface(); + + /** + * Get the i-th interface from the list. + * @param interfaceIndex index of interface, from 0 to number of interface - 1. + */ + nsINetworkInterface getInterface(in long interfaceIndex); +}; + +[scriptable, uuid(5be50bcb-bfe9-4742-b7e6-3e9bb4835369)] +interface nsINetworkInterfaceListService : nsISupports +{ + const long LIST_NOT_INCLUDE_MMS_INTERFACES = 1; + const long LIST_NOT_INCLUDE_SUPL_INTERFACES = 2; + + /** + * Obtain a list of network interfaces that satisfy the specified condition. + * @param condition flags that specify the interfaces to be returned. This + * can be OR combination of LIST_* flags, or zero to make all available + * interfaces returned. + */ + nsINetworkInterfaceList getDataInterfaceList(in long condition); +}; diff --git a/media/mtransport/gonk_addrs.cpp b/media/mtransport/gonk_addrs.cpp new file mode 100644 index 00000000000..b3b4b5e429f --- /dev/null +++ b/media/mtransport/gonk_addrs.cpp @@ -0,0 +1,129 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ +extern "C" { +#include +#include "r_types.h" +#include "stun.h" +#include "addrs.h" +} + +#include +#include +#include "nsINetworkManager.h" +#include "nsINetworkInterfaceListService.h" +#include "runnable_utils.h" +#include "nsCOMPtr.h" +#include "nsThreadUtils.h" +#include "nsServiceManagerUtils.h" + +namespace { +struct NetworkInterface { + struct sockaddr_in addr; + std::string name; +}; + +nsresult +GetInterfaces(std::vector* aInterfaces) +{ + MOZ_ASSERT(aInterfaces); + + // Obtain network interfaces from network manager. + nsresult rv; + nsCOMPtr listService = + do_GetService("@mozilla.org/network/interface-list-service;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + int32_t flags = + nsINetworkInterfaceListService::LIST_NOT_INCLUDE_SUPL_INTERFACES | + nsINetworkInterfaceListService::LIST_NOT_INCLUDE_MMS_INTERFACES; + nsCOMPtr networkList; + NS_ENSURE_SUCCESS(listService->GetDataInterfaceList(flags, + getter_AddRefs(networkList)), + NS_ERROR_FAILURE); + + // Translate nsINetworkInterfaceList to NetworkInterface. + int32_t listLength; + NS_ENSURE_SUCCESS(networkList->GetNumberOfInterface(&listLength), + NS_ERROR_FAILURE); + aInterfaces->clear(); + + nsAutoString ip; + nsAutoString ifaceName; + for (int32_t i = 0; i < listLength; i++) { + nsCOMPtr iface; + if (NS_FAILED(networkList->GetInterface(i, getter_AddRefs(iface)))) { + continue; + } + + NetworkInterface interface; + memset(&(interface.addr), 0, sizeof(interface.addr)); + interface.addr.sin_family = AF_INET; + if (NS_FAILED(iface->GetIp(ip))) { + continue; + } + if (inet_pton(AF_INET, NS_ConvertUTF16toUTF8(ip).get(), + &(interface.addr.sin_addr.s_addr)) != 1) { + continue; + } + + if (NS_FAILED(iface->GetName(ifaceName))) { + continue; + } + interface.name = NS_ConvertUTF16toUTF8(ifaceName).get(); + + aInterfaces->push_back(interface); + } + return NS_OK; +} +} // anonymous namespace + +int +nr_stun_get_addrs(nr_transport_addr aAddrs[], int aMaxAddrs, + int aDropLoopback, int* aCount) +{ + nsresult rv; + int r; + + // Get network interface list. + std::vector interfaces; + if (NS_FAILED(NS_DispatchToMainThread( + mozilla::WrapRunnableNMRet(&GetInterfaces, &interfaces, &rv), + NS_DISPATCH_SYNC))) { + return R_FAILED; + } + + if (NS_FAILED(rv)) { + return R_FAILED; + } + + // Translate to nr_transport_addr. + int32_t n = 0; + size_t num_interface = std::min(interfaces.size(), (size_t)aMaxAddrs); + for (size_t i = 0; i < num_interface; ++i) { + NetworkInterface &interface = interfaces[i]; + if (nr_sockaddr_to_transport_addr((sockaddr*)&(interface.addr), + sizeof(struct sockaddr_in), + IPPROTO_UDP, 0, &(aAddrs[n]))) { + r_log(NR_LOG_STUN, LOG_WARNING, "Problem transforming address"); + return R_FAILED; + } + strlcpy(aAddrs[n].ifname, interface.name.c_str(), sizeof(aAddrs[n].ifname)); + n++; + } + + *aCount = n; + r = nr_stun_remove_duplicate_addrs(aAddrs, aDropLoopback, aCount); + if (r != 0) { + return r; + } + + for (int i = 0; i < *aCount; ++i) { + r_log(NR_LOG_STUN, LOG_DEBUG, "Address %d: %s on %s", i, + aAddrs[i].as_string, aAddrs[i].ifname); + } + + return 0; +} diff --git a/media/mtransport/objs.mk b/media/mtransport/objs.mk index 334b68b57f5..3541bc04d75 100644 --- a/media/mtransport/objs.mk +++ b/media/mtransport/objs.mk @@ -79,5 +79,11 @@ MTRANSPORT_LCPPSRCS = \ transportlayerprsock.cpp \ $(NULL) +ifdef MOZ_B2G_RIL +MTRANSPORT_LCPPSRCS += \ + gonk_addrs.cpp \ + $(NULL) +endif + MTRANSPORT_CPPSRCS = $(addprefix $(topsrcdir)/media/mtransport/, $(MTRANSPORT_LCPPSRCS)) diff --git a/media/mtransport/third_party/nICEr/nicer.gyp b/media/mtransport/third_party/nICEr/nicer.gyp index a256476cc88..a8f163ba688 100644 --- a/media/mtransport/third_party/nICEr/nicer.gyp +++ b/media/mtransport/third_party/nICEr/nicer.gyp @@ -6,6 +6,9 @@ # # { + 'variables' : { + 'build_with_gonk%': 0, + }, 'targets' : [ { 'target_name' : 'nicer', @@ -213,6 +216,12 @@ 'NO_REG_RPC', ], }], + # Gonk has its own nr_stun_get_addrs implementation. + ['build_with_gonk==1', { + 'defines': [ + "USE_PLATFORM_NR_STUN_GET_ADDRS", + ] + }] ], }] } diff --git a/media/mtransport/third_party/nICEr/src/stun/addrs.c b/media/mtransport/third_party/nICEr/src/stun/addrs.c index c2bba99fede..ce0ed0d6117 100644 --- a/media/mtransport/third_party/nICEr/src/stun/addrs.c +++ b/media/mtransport/third_party/nICEr/src/stun/addrs.c @@ -687,6 +687,8 @@ nr_stun_remove_duplicate_addrs(nr_transport_addr addrs[], int remove_loopback, i return _status; } +#ifndef USE_PLATFORM_NR_STUN_GET_ADDRS + int nr_stun_get_addrs(nr_transport_addr addrs[], int maxaddrs, int drop_loopback, int *count) { @@ -712,3 +714,4 @@ nr_stun_get_addrs(nr_transport_addr addrs[], int maxaddrs, int drop_loopback, in return _status; } +#endif