зеркало из https://github.com/mozilla/gecko-dev.git
Bug 869615 - Sanitize Contact properties everywhere, not just in init. r=bent
--HG-- extra : rebase_source : 25fd18299c15cee78dff008460c89e78ba60d842
This commit is contained in:
Родитель
73f4fbc038
Коммит
96a3e4af42
|
@ -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 () {
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
@ -331,7 +322,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 +489,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;
|
||||
|
@ -1091,7 +1082,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 +1246,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 +1421,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 +1453,11 @@ function next() {
|
|||
return;
|
||||
}
|
||||
try {
|
||||
steps[index]();
|
||||
var i = index++;
|
||||
steps[i]();
|
||||
} catch(ex) {
|
||||
ok(false, "Caught exception", ex);
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
|
Загрузка…
Ссылка в новой задаче