зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to m-i
This commit is contained in:
Коммит
72e4c181e6
|
@ -19,288 +19,28 @@ XPCOMUtils.defineLazyServiceGetter(Services, "DOMRequest",
|
|||
"@mozilla.org/dom/dom-request-service;1",
|
||||
"nsIDOMRequestService");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "pm",
|
||||
"@mozilla.org/permissionmanager;1",
|
||||
"nsIPermissionManager");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"@mozilla.org/childprocessmessagemanager;1",
|
||||
"nsIMessageSender");
|
||||
|
||||
const CONTACTS_SENDMORE_MINIMUM = 5;
|
||||
|
||||
function ContactAddressImpl() { }
|
||||
|
||||
ContactAddressImpl.prototype = {
|
||||
// This function is meant to be called via bindings code for type checking,
|
||||
// don't call it directly. Instead, create a content object and call initialize
|
||||
// on that.
|
||||
initialize: function(aType, aStreetAddress, aLocality, aRegion, aPostalCode, aCountryName, aPref) {
|
||||
this.type = aType;
|
||||
this.streetAddress = aStreetAddress;
|
||||
this.locality = aLocality;
|
||||
this.region = aRegion;
|
||||
this.postalCode = aPostalCode;
|
||||
this.countryName = aCountryName;
|
||||
this.pref = aPref;
|
||||
},
|
||||
|
||||
toJSON: function(excludeExposedProps) {
|
||||
let json = {
|
||||
type: this.type,
|
||||
streetAddress: this.streetAddress,
|
||||
locality: this.locality,
|
||||
region: this.region,
|
||||
postalCode: this.postalCode,
|
||||
countryName: this.countryName,
|
||||
pref: this.pref,
|
||||
};
|
||||
if (!excludeExposedProps) {
|
||||
json.__exposedProps__ = {
|
||||
type: "rw",
|
||||
streetAddress: "rw",
|
||||
locality: "rw",
|
||||
region: "rw",
|
||||
postalCode: "rw",
|
||||
countryName: "rw",
|
||||
pref: "rw",
|
||||
};
|
||||
}
|
||||
return json;
|
||||
},
|
||||
|
||||
classID: Components.ID("{9cbfa81c-bcab-4ca9-b0d2-f4318f295e33}"),
|
||||
contractID: "@mozilla.org/contactAddress;1",
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
|
||||
};
|
||||
|
||||
function ContactFieldImpl() { }
|
||||
|
||||
ContactFieldImpl.prototype = {
|
||||
// This function is meant to be called via bindings code for type checking,
|
||||
// don't call it directly. Instead, create a content object and call initialize
|
||||
// on that.
|
||||
initialize: function(aType, aValue, aPref) {
|
||||
this.type = aType;
|
||||
this.value = aValue;
|
||||
this.pref = aPref;
|
||||
},
|
||||
|
||||
toJSON: function(excludeExposedProps) {
|
||||
let json = {
|
||||
type: this.type,
|
||||
value: this.value,
|
||||
pref: this.pref,
|
||||
};
|
||||
if (!excludeExposedProps) {
|
||||
json.__exposedProps__ = {
|
||||
type: "rw",
|
||||
value: "rw",
|
||||
pref: "rw",
|
||||
};
|
||||
}
|
||||
return json;
|
||||
},
|
||||
|
||||
classID: Components.ID("{ad19a543-69e4-44f0-adfa-37c011556bc1}"),
|
||||
contractID: "@mozilla.org/contactField;1",
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
|
||||
};
|
||||
|
||||
function ContactTelFieldImpl() { }
|
||||
|
||||
ContactTelFieldImpl.prototype = {
|
||||
// This function is meant to be called via bindings code for type checking,
|
||||
// don't call it directly. Instead, create a content object and call initialize
|
||||
// on that.
|
||||
initialize: function(aType, aValue, aCarrier, aPref) {
|
||||
this.type = aType;
|
||||
this.value = aValue;
|
||||
this.carrier = aCarrier;
|
||||
this.pref = aPref;
|
||||
},
|
||||
|
||||
toJSON: function(excludeExposedProps) {
|
||||
let json = {
|
||||
type: this.type,
|
||||
value: this.value,
|
||||
carrier: this.carrier,
|
||||
pref: this.pref,
|
||||
};
|
||||
if (!excludeExposedProps) {
|
||||
json.__exposedProps__ = {
|
||||
type: "rw",
|
||||
value: "rw",
|
||||
carrier: "rw",
|
||||
pref: "rw",
|
||||
};
|
||||
}
|
||||
return json;
|
||||
},
|
||||
|
||||
classID: Components.ID("{4d42c5a9-ea5d-4102-80c3-40cc986367ca}"),
|
||||
contractID: "@mozilla.org/contactTelField;1",
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
|
||||
};
|
||||
|
||||
function validateArrayField(data, createCb) {
|
||||
// We use an array-like Proxy to validate data set by content, since we don't
|
||||
// have WebIDL arrays yet. See bug 851726.
|
||||
|
||||
// ArrayPropertyExposedPropsProxy is used to return "rw" for any valid index
|
||||
// and "length" in __exposedProps__.
|
||||
const ArrayPropertyExposedPropsProxy = new Proxy({}, {
|
||||
get: function(target, name) {
|
||||
// Test for index access
|
||||
if (String(name >>> 0) === name) {
|
||||
return "rw";
|
||||
}
|
||||
if (name === "length") {
|
||||
return "r";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const ArrayPropertyHandler = {
|
||||
set: function(target, name, val, receiver) {
|
||||
// Test for index access
|
||||
if (String(name >>> 0) === name) {
|
||||
target[name] = createCb(val);
|
||||
}
|
||||
},
|
||||
get: function(target, name) {
|
||||
if (name === "__exposedProps__") {
|
||||
return ArrayPropertyExposedPropsProxy;
|
||||
}
|
||||
return target[name];
|
||||
}
|
||||
};
|
||||
|
||||
if (data === null || data === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
data = Array.isArray(data) ? data : [data];
|
||||
let filtered = [];
|
||||
for (let i = 0, n = data.length; i < n; ++i) {
|
||||
if (data[i]) {
|
||||
filtered.push(createCb(data[i]));
|
||||
}
|
||||
}
|
||||
return new Proxy(filtered, ArrayPropertyHandler);
|
||||
}
|
||||
|
||||
// We need this to create a copy of the mozContact object in ContactManager.save
|
||||
// Keep in sync with the interfaces.
|
||||
const PROPERTIES = [
|
||||
"name", "honorificPrefix", "givenName", "additionalName", "familyName",
|
||||
"honorificSuffix", "nickname", "photo", "category", "org", "jobTitle",
|
||||
"bday", "note", "anniversary", "sex", "genderIdentity", "key"
|
||||
"bday", "note", "anniversary", "sex", "genderIdentity", "key", "adr", "email",
|
||||
"url", "impp", "tel"
|
||||
];
|
||||
const ADDRESS_PROPERTIES = ["adr"];
|
||||
const FIELD_PROPERTIES = ["email", "url", "impp"];
|
||||
const TELFIELD_PROPERTIES = ["tel"];
|
||||
|
||||
function Contact() { }
|
||||
|
||||
Contact.prototype = {
|
||||
// We need to create the content interfaces in these setters, otherwise when
|
||||
// we return these objects (e.g. from a find call), the values in the array
|
||||
// will be COW's, and content cannot see the properties.
|
||||
set email(aEmail) {
|
||||
this._email = aEmail;
|
||||
},
|
||||
|
||||
get email() {
|
||||
this._email = validateArrayField(this._email, function(email) {
|
||||
let obj = this._window.ContactField._create(this._window, new ContactFieldImpl());
|
||||
obj.initialize(email.type, email.value, email.pref);
|
||||
return obj;
|
||||
}.bind(this));
|
||||
return this._email;
|
||||
},
|
||||
|
||||
set adr(aAdr) {
|
||||
this._adr = aAdr;
|
||||
},
|
||||
|
||||
get adr() {
|
||||
this._adr = validateArrayField(this._adr, function(adr) {
|
||||
let obj = this._window.ContactAddress._create(this._window, new ContactAddressImpl());
|
||||
obj.initialize(adr.type, adr.streetAddress, adr.locality,
|
||||
adr.region, adr.postalCode, adr.countryName,
|
||||
adr.pref);
|
||||
return obj;
|
||||
}.bind(this));
|
||||
return this._adr;
|
||||
},
|
||||
|
||||
set tel(aTel) {
|
||||
this._tel = aTel;
|
||||
},
|
||||
|
||||
get tel() {
|
||||
this._tel = validateArrayField(this._tel, function(tel) {
|
||||
let obj = this._window.ContactTelField._create(this._window, new ContactTelFieldImpl());
|
||||
obj.initialize(tel.type, tel.value, tel.carrier, tel.pref);
|
||||
return obj;
|
||||
}.bind(this));
|
||||
return this._tel;
|
||||
},
|
||||
|
||||
set impp(aImpp) {
|
||||
this._impp = aImpp;
|
||||
},
|
||||
|
||||
get impp() {
|
||||
this._impp = validateArrayField(this._impp, function(impp) {
|
||||
let obj = this._window.ContactField._create(this._window, new ContactFieldImpl());
|
||||
obj.initialize(impp.type, impp.value, impp.pref);
|
||||
return obj;
|
||||
}.bind(this));
|
||||
return this._impp;
|
||||
},
|
||||
|
||||
set url(aUrl) {
|
||||
this._url = aUrl;
|
||||
},
|
||||
|
||||
get url() {
|
||||
this._url = validateArrayField(this._url, function(url) {
|
||||
let obj = this._window.ContactField._create(this._window, new ContactFieldImpl());
|
||||
obj.initialize(url.type, url.value, url.pref);
|
||||
return obj;
|
||||
}.bind(this));
|
||||
return this._url;
|
||||
},
|
||||
|
||||
init: function(aWindow) {
|
||||
this._window = aWindow;
|
||||
},
|
||||
|
||||
__init: function(aProp) {
|
||||
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;
|
||||
this.key = aProp.key;
|
||||
for (let prop in aProp) {
|
||||
this[prop] = aProp[prop];
|
||||
}
|
||||
},
|
||||
|
||||
setMetadata: function(aId, aPublished, aUpdated) {
|
||||
|
@ -313,69 +53,9 @@ Contact.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
toJSON: function() {
|
||||
return {
|
||||
id: this.id,
|
||||
published: this.published,
|
||||
updated: this.updated,
|
||||
|
||||
name: this.name,
|
||||
honorificPrefix: this.honorificPrefix,
|
||||
givenName: this.givenName,
|
||||
additionalName: this.additionalName,
|
||||
familyName: this.familyName,
|
||||
honorificSuffix: this.honorificSuffix,
|
||||
nickname: this.nickname,
|
||||
category: this.category,
|
||||
org: this.org,
|
||||
jobTitle: this.jobTitle,
|
||||
note: this.note,
|
||||
sex: this.sex,
|
||||
genderIdentity: this.genderIdentity,
|
||||
email: this.email,
|
||||
photo: this.photo,
|
||||
adr: this.adr,
|
||||
url: this.url,
|
||||
tel: this.tel,
|
||||
bday: this.bday,
|
||||
impp: this.impp,
|
||||
anniversary: this.anniversary,
|
||||
key: this.key,
|
||||
|
||||
__exposedProps__: {
|
||||
id: "rw",
|
||||
published: "rw",
|
||||
updated: "rw",
|
||||
name: "rw",
|
||||
honorificPrefix: "rw",
|
||||
givenName: "rw",
|
||||
additionalName: "rw",
|
||||
familyName: "rw",
|
||||
honorificSuffix: "rw",
|
||||
nickname: "rw",
|
||||
category: "rw",
|
||||
org: "rw",
|
||||
jobTitle: "rw",
|
||||
note: "rw",
|
||||
sex: "rw",
|
||||
genderIdentity: "rw",
|
||||
email: "rw",
|
||||
photo: "rw",
|
||||
adr: "rw",
|
||||
url: "rw",
|
||||
tel: "rw",
|
||||
bday: "rw",
|
||||
impp: "rw",
|
||||
anniversary: "rw",
|
||||
key: "rw",
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
classID: Components.ID("{72a5ee28-81d8-4af8-90b3-ae935396cc66}"),
|
||||
contractID: "@mozilla.org/contact;1",
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
|
||||
Ci.nsIDOMGlobalPropertyInitializer]),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
|
||||
};
|
||||
|
||||
function ContactManager() { }
|
||||
|
@ -599,50 +279,19 @@ ContactManager.prototype = {
|
|||
// mozContact object.
|
||||
let newContact = {properties: {}};
|
||||
|
||||
for (let field of PROPERTIES) {
|
||||
if (aContact[field]) {
|
||||
try {
|
||||
for (let field of PROPERTIES) {
|
||||
// This hack makes sure modifications to the sequence attributes get validated.
|
||||
aContact[field] = aContact[field];
|
||||
newContact.properties[field] = aContact[field];
|
||||
}
|
||||
}
|
||||
|
||||
for (let prop of ADDRESS_PROPERTIES) {
|
||||
if (aContact[prop]) {
|
||||
newContact.properties[prop] = [];
|
||||
for (let i of aContact[prop]) {
|
||||
if (i) {
|
||||
let json = ContactAddressImpl.prototype.toJSON.apply(i, [true]);
|
||||
newContact.properties[prop].push(json);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let prop of FIELD_PROPERTIES) {
|
||||
if (aContact[prop]) {
|
||||
newContact.properties[prop] = [];
|
||||
for (let i of aContact[prop]) {
|
||||
if (i) {
|
||||
let json = ContactFieldImpl.prototype.toJSON.apply(i, [true]);
|
||||
newContact.properties[prop].push(json);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let prop of TELFIELD_PROPERTIES) {
|
||||
if (aContact[prop]) {
|
||||
newContact.properties[prop] = [];
|
||||
for (let i of aContact[prop]) {
|
||||
if (i) {
|
||||
let json = ContactTelFieldImpl.prototype.toJSON.apply(i, [true]);
|
||||
newContact.properties[prop].push(json);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// And then make sure we throw a proper error message (no internal file and line #)
|
||||
throw new this._window.DOMError(e.name, e.message);
|
||||
}
|
||||
|
||||
let request = this.createRequest();
|
||||
let requestID = this.getRequestId({request: request, reason: reason});
|
||||
let requestID = this.getRequestId({request: request});
|
||||
|
||||
let reason;
|
||||
if (aContact.id == "undefined") {
|
||||
|
@ -816,5 +465,5 @@ ContactManager.prototype = {
|
|||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([
|
||||
Contact, ContactManager, ContactFieldImpl, ContactAddressImpl, ContactTelFieldImpl
|
||||
Contact, ContactManager
|
||||
]);
|
||||
|
|
|
@ -1,12 +1,3 @@
|
|||
component {9cbfa81c-bcab-4ca9-b0d2-f4318f295e33} ContactManager.js
|
||||
contract @mozilla.org/contactAddress;1 {9cbfa81c-bcab-4ca9-b0d2-f4318f295e33}
|
||||
|
||||
component {ad19a543-69e4-44f0-adfa-37c011556bc1} ContactManager.js
|
||||
contract @mozilla.org/contactField;1 {ad19a543-69e4-44f0-adfa-37c011556bc1}
|
||||
|
||||
component {4d42c5a9-ea5d-4102-80c3-40cc986367ca} ContactManager.js
|
||||
contract @mozilla.org/contactTelField;1 {4d42c5a9-ea5d-4102-80c3-40cc986367ca}
|
||||
|
||||
component {72a5ee28-81d8-4af8-90b3-ae935396cc66} ContactManager.js
|
||||
contract @mozilla.org/contact;1 {72a5ee28-81d8-4af8-90b3-ae935396cc66}
|
||||
|
||||
|
|
|
@ -552,7 +552,8 @@ var steps = [
|
|||
honorificPrefix: [],
|
||||
honorificSuffix: [{foo: "bar"}],
|
||||
sex: 17,
|
||||
genderIdentity: 18
|
||||
genderIdentity: 18,
|
||||
email: [{type: ["foo"], value: "bar"}]
|
||||
};
|
||||
obj.honorificPrefix.__defineGetter__('0',(function() {
|
||||
var c = 0;
|
||||
|
@ -566,13 +567,15 @@ var steps = [
|
|||
}
|
||||
})());
|
||||
createResult1 = new mozContact(obj);
|
||||
createResult1.email.push({aeiou: "abcde"});
|
||||
req = mozContacts.save(createResult1);
|
||||
req.onsuccess = function () {
|
||||
checkContacts(createResult1, {
|
||||
honorificPrefix: ["string"],
|
||||
honorificSuffix: ["[object Object]"],
|
||||
sex: "17",
|
||||
genderIdentity: "18"
|
||||
genderIdentity: "18",
|
||||
email: [{type: ["foo"], value: "bar"}, {}]
|
||||
});
|
||||
next();
|
||||
};
|
||||
|
@ -696,13 +699,13 @@ var steps = [
|
|||
impp: [{value: undefined}],
|
||||
tel: [{value: undefined}],
|
||||
});
|
||||
ise(c.adr[0].streetAddress, null, "adr.streetAddress is null");
|
||||
ise(c.adr[0].locality, null, "adr.locality is null");
|
||||
ise(c.adr[0].pref, null, "adr.pref is null");
|
||||
ise(c.email[0].value, null, "email.value is null");
|
||||
ise(c.url[0].value, null, "url.value is null");
|
||||
ise(c.impp[0].value, null, "impp.value is null");
|
||||
ise(c.tel[0].value, null, "tel.value is null");
|
||||
ise(c.adr[0].streetAddress, undefined, "adr.streetAddress is undefined");
|
||||
ise(c.adr[0].locality, undefined, "adr.locality is undefined");
|
||||
ise(c.adr[0].pref, undefined, "adr.pref is undefined");
|
||||
ise(c.email[0].value, undefined, "email.value is undefined");
|
||||
ise(c.url[0].value, undefined, "url.value is undefined");
|
||||
ise(c.impp[0].value, undefined, "impp.value is undefined");
|
||||
ise(c.tel[0].value, undefined, "tel.value is undefined");
|
||||
next();
|
||||
},
|
||||
function() {
|
||||
|
@ -721,6 +724,50 @@ var steps = [
|
|||
testArrayProp("url");
|
||||
next();
|
||||
},
|
||||
function() {
|
||||
ok(true, "Passing a mozContact with invalid data to save() should throw");
|
||||
var c = new mozContact({
|
||||
photo: [],
|
||||
tel: []
|
||||
});
|
||||
c.photo.push({});
|
||||
SimpleTest.doesThrow(()=>navigator.mozContacts.save(c), "Invalid data in Blob array");
|
||||
c.tel.push(123);
|
||||
SimpleTest.doesThrow(()=>navigator.mozContacts.save(c), "Invalid data in dictionary array");
|
||||
next();
|
||||
},
|
||||
function() {
|
||||
ok(true, "Inline changes to array properties should be seen by save");
|
||||
var c = new mozContact({
|
||||
name: [],
|
||||
familyName: [],
|
||||
givenName: [],
|
||||
nickname: [],
|
||||
tel: [],
|
||||
adr: [],
|
||||
email: []
|
||||
});
|
||||
for (var prop of Object.getOwnPropertyNames(properties1)) {
|
||||
if (!Array.isArray(properties1[prop])) {
|
||||
continue;
|
||||
}
|
||||
for (var i = 0; i < properties1[prop].length; ++i) {
|
||||
c[prop].push(properties1[prop][i]);
|
||||
}
|
||||
}
|
||||
req = navigator.mozContacts.save(c);
|
||||
req.onsuccess = function() {
|
||||
req = navigator.mozContacts.find(defaultOptions);
|
||||
req.onsuccess = function() {
|
||||
ise(req.result.length, 1, "Got 1 contact");
|
||||
checkContacts(req.result[0], properties1);
|
||||
next();
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
};
|
||||
req.onerror = onFailure;
|
||||
},
|
||||
clearDatabase,
|
||||
function () {
|
||||
ok(true, "all done!\n");
|
||||
SimpleTest.finish();
|
||||
|
|
|
@ -4,29 +4,7 @@
|
|||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
[ChromeOnly, Constructor, JSImplementation="@mozilla.org/contactAddress;1"]
|
||||
interface ContactAddress {
|
||||
attribute object? type; // DOMString[]
|
||||
attribute DOMString? streetAddress;
|
||||
attribute DOMString? locality;
|
||||
attribute DOMString? region;
|
||||
attribute DOMString? postalCode;
|
||||
attribute DOMString? countryName;
|
||||
attribute boolean? pref;
|
||||
|
||||
[ChromeOnly]
|
||||
void initialize(optional sequence<DOMString>? type,
|
||||
optional DOMString? streetAddress,
|
||||
optional DOMString? locality,
|
||||
optional DOMString? region,
|
||||
optional DOMString? postalCode,
|
||||
optional DOMString? countryName,
|
||||
optional boolean? pref);
|
||||
|
||||
object toJSON();
|
||||
};
|
||||
|
||||
dictionary ContactAddressInit {
|
||||
dictionary ContactAddress {
|
||||
sequence<DOMString>? type;
|
||||
DOMString? streetAddress;
|
||||
DOMString? locality;
|
||||
|
@ -36,46 +14,16 @@ dictionary ContactAddressInit {
|
|||
boolean? pref;
|
||||
};
|
||||
|
||||
|
||||
[ChromeOnly, Constructor, JSImplementation="@mozilla.org/contactField;1"]
|
||||
interface ContactField {
|
||||
attribute object? type; // DOMString[]
|
||||
attribute DOMString? value;
|
||||
attribute boolean? pref;
|
||||
|
||||
[ChromeOnly]
|
||||
void initialize(optional sequence<DOMString>? type,
|
||||
optional DOMString? value,
|
||||
optional boolean? pref);
|
||||
|
||||
object toJSON();
|
||||
};
|
||||
|
||||
dictionary ContactFieldInit {
|
||||
dictionary ContactField {
|
||||
sequence<DOMString>? type;
|
||||
DOMString? value;
|
||||
boolean? pref;
|
||||
};
|
||||
|
||||
|
||||
[ChromeOnly, Constructor, JSImplementation="@mozilla.org/contactTelField;1"]
|
||||
interface ContactTelField : ContactField {
|
||||
attribute DOMString? carrier;
|
||||
|
||||
[ChromeOnly]
|
||||
void initialize(optional sequence<DOMString>? type,
|
||||
optional DOMString? value,
|
||||
optional DOMString? carrier,
|
||||
optional boolean? pref);
|
||||
|
||||
object toJSON();
|
||||
};
|
||||
|
||||
dictionary ContactTelFieldInit : ContactFieldInit {
|
||||
dictionary ContactTelField : ContactField {
|
||||
DOMString? carrier;
|
||||
};
|
||||
|
||||
|
||||
dictionary ContactProperties {
|
||||
Date? bday;
|
||||
Date? anniversary;
|
||||
|
@ -85,13 +33,13 @@ dictionary ContactProperties {
|
|||
|
||||
sequence<Blob>? photo;
|
||||
|
||||
sequence<ContactAddressInit>? adr;
|
||||
sequence<ContactAddress>? adr;
|
||||
|
||||
sequence<ContactFieldInit>? email;
|
||||
sequence<ContactFieldInit>? url;
|
||||
sequence<ContactFieldInit>? impp;
|
||||
sequence<ContactField>? email;
|
||||
sequence<ContactField>? url;
|
||||
sequence<ContactField>? impp;
|
||||
|
||||
sequence<ContactTelFieldInit>? tel;
|
||||
sequence<ContactTelField>? tel;
|
||||
|
||||
sequence<DOMString>? name;
|
||||
sequence<DOMString>? honorificPrefix;
|
||||
|
@ -110,43 +58,43 @@ dictionary ContactProperties {
|
|||
[Constructor(optional ContactProperties properties),
|
||||
JSImplementation="@mozilla.org/contact;1"]
|
||||
interface mozContact {
|
||||
attribute DOMString id;
|
||||
readonly attribute Date? published;
|
||||
readonly attribute Date? updated;
|
||||
attribute DOMString id;
|
||||
readonly attribute Date? published;
|
||||
readonly attribute Date? updated;
|
||||
|
||||
attribute Date? bday;
|
||||
attribute Date? anniversary;
|
||||
attribute Date? bday;
|
||||
attribute Date? anniversary;
|
||||
|
||||
attribute DOMString? sex;
|
||||
attribute DOMString? genderIdentity;
|
||||
attribute DOMString? sex;
|
||||
attribute DOMString? genderIdentity;
|
||||
|
||||
attribute object? photo;
|
||||
[Cached, Pure] attribute sequence<Blob>? photo;
|
||||
|
||||
attribute object? adr;
|
||||
[Cached, Pure] attribute sequence<ContactAddress>? adr;
|
||||
|
||||
attribute object? email;
|
||||
attribute object? url;
|
||||
attribute object? impp;
|
||||
[Cached, Pure] attribute sequence<ContactField>? email;
|
||||
[Cached, Pure] attribute sequence<ContactField>? url;
|
||||
[Cached, Pure] attribute sequence<ContactField>? impp;
|
||||
|
||||
attribute object? tel;
|
||||
[Cached, Pure] attribute sequence<ContactTelField>? tel;
|
||||
|
||||
attribute object? name;
|
||||
attribute object? honorificPrefix;
|
||||
attribute object? givenName;
|
||||
attribute object? additionalName;
|
||||
attribute object? familyName;
|
||||
attribute object? honorificSuffix;
|
||||
attribute object? nickname;
|
||||
attribute object? category;
|
||||
attribute object? org;
|
||||
attribute object? jobTitle;
|
||||
attribute object? note;
|
||||
attribute object? key;
|
||||
[Cached, Pure] attribute sequence<DOMString>? name;
|
||||
[Cached, Pure] attribute sequence<DOMString>? honorificPrefix;
|
||||
[Cached, Pure] attribute sequence<DOMString>? givenName;
|
||||
[Cached, Pure] attribute sequence<DOMString>? additionalName;
|
||||
[Cached, Pure] attribute sequence<DOMString>? familyName;
|
||||
[Cached, Pure] attribute sequence<DOMString>? honorificSuffix;
|
||||
[Cached, Pure] attribute sequence<DOMString>? nickname;
|
||||
[Cached, Pure] attribute sequence<DOMString>? category;
|
||||
[Cached, Pure] attribute sequence<DOMString>? org;
|
||||
[Cached, Pure] attribute sequence<DOMString>? jobTitle;
|
||||
[Cached, Pure] attribute sequence<DOMString>? note;
|
||||
[Cached, Pure] attribute sequence<DOMString>? key;
|
||||
|
||||
[ChromeOnly]
|
||||
void setMetadata(DOMString id, Date? published, Date? updated);
|
||||
|
||||
object toJSON();
|
||||
jsonifier;
|
||||
};
|
||||
|
||||
dictionary ContactFindSortOptions {
|
||||
|
|
|
@ -637,6 +637,7 @@ public class Tab {
|
|||
clearFavicon();
|
||||
|
||||
setHasFeeds(false);
|
||||
setHasOpenSearch(false);
|
||||
updateTitle(null);
|
||||
updateIdentityData(null);
|
||||
setReaderEnabled(false);
|
||||
|
|
|
@ -18,8 +18,7 @@
|
|||
android:title="@string/contextmenu_subscribe"/>
|
||||
|
||||
<item android:id="@+id/add_search_engine"
|
||||
android:title="@string/contextmenu_add_search_engine"
|
||||
android:visible="false"/>
|
||||
android:title="@string/contextmenu_add_search_engine"/>
|
||||
|
||||
<item android:id="@+id/copyurl"
|
||||
android:title="@string/contextmenu_copyurl"/>
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head id="linkparent">
|
||||
<title>Autodiscovery Test</title>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
|
@ -67,6 +67,7 @@ skip-if = processor == "x86"
|
|||
# [testVkbOverlap] # see bug 907274
|
||||
|
||||
# Using JavascriptTest
|
||||
[testBrowserDiscovery]
|
||||
[testDeviceSearchEngine]
|
||||
[testJNI]
|
||||
# [testMozPay] # see bug 945675
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package org.mozilla.gecko.tests;
|
||||
|
||||
import org.mozilla.gecko.*;
|
||||
|
||||
|
||||
public class testBrowserDiscovery extends JavascriptTest {
|
||||
public testBrowserDiscovery() {
|
||||
super("testBrowserDiscovery.js");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
|
||||
/* 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 } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function ok(passed, text) {
|
||||
do_report_result(passed, text, Components.stack.caller, false);
|
||||
}
|
||||
|
||||
// We use a global variable to track the <browser> where the tests are happening
|
||||
let browser;
|
||||
|
||||
function setHandlerFunc(handler, test) {
|
||||
browser.addEventListener("DOMLinkAdded", function linkAdded(event) {
|
||||
browser.removeEventListener("DOMLinkAdded", linkAdded, false);
|
||||
Services.tm.mainThread.dispatch(handler.bind(this, test), Ci.nsIThread.DISPATCH_NORMAL);
|
||||
}, false);
|
||||
}
|
||||
|
||||
add_test(function setup_browser() {
|
||||
let BrowserApp = Services.wm.getMostRecentWindow("navigator:browser").BrowserApp;
|
||||
do_register_cleanup(function cleanup() {
|
||||
BrowserApp.closeTab(BrowserApp.getTabForBrowser(browser));
|
||||
});
|
||||
|
||||
let url = "http://mochi.test:8888/tests/robocop/link_discovery.html";
|
||||
browser = BrowserApp.addTab(url, { selected: true, parentId: BrowserApp.selectedTab.id }).browser;
|
||||
browser.addEventListener("load", function startTests(event) {
|
||||
browser.removeEventListener("load", startTests, true);
|
||||
Services.tm.mainThread.dispatch(run_next_test, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
}, true);
|
||||
});
|
||||
|
||||
let searchDiscoveryTests = [
|
||||
{ text: "rel search discovered" },
|
||||
{ rel: "SEARCH", text: "rel is case insensitive" },
|
||||
{ rel: "-search-", pass: false, text: "rel -search- not discovered" },
|
||||
{ rel: "foo bar baz search quux", text: "rel may contain additional rels separated by spaces" },
|
||||
{ href: "https://not.mozilla.com", text: "HTTPS ok" },
|
||||
{ href: "ftp://not.mozilla.com", text: "FTP ok" },
|
||||
{ href: "data:text/foo,foo", pass: false, text: "data URI not permitted" },
|
||||
{ href: "javascript:alert(0)", pass: false, text: "JS URI not permitted" },
|
||||
{ type: "APPLICATION/OPENSEARCHDESCRIPTION+XML", text: "type is case insensitve" },
|
||||
{ type: " application/opensearchdescription+xml ", text: "type may contain extra whitespace" },
|
||||
{ type: "application/opensearchdescription+xml; charset=utf-8", text: "type may have optional parameters (RFC2046)" },
|
||||
{ type: "aapplication/opensearchdescription+xml", pass: false, text: "type should not be loosely matched" },
|
||||
{ rel: "search search search", count: 1, text: "only one engine should be added" }
|
||||
];
|
||||
|
||||
function execute_search_test(test) {
|
||||
if (browser.engines) {
|
||||
let matchCount = (!("count" in test) || browser.engines.length === test.count);
|
||||
let matchTitle = (test.title == browser.engines[0].title);
|
||||
ok(matchCount && matchTitle, test.text);
|
||||
browser.engines = null;
|
||||
} else {
|
||||
ok(!test.pass, test.text);
|
||||
}
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function prep_search_test(test) {
|
||||
setHandlerFunc(execute_search_test, test);
|
||||
|
||||
let rel = test.rel || "search";
|
||||
let href = test.href || "http://so.not.here.mozilla.com/search.xml";
|
||||
let type = test.type || "application/opensearchdescription+xml";
|
||||
let title = test.title;
|
||||
if (!("pass" in test)) {
|
||||
test.pass = true;
|
||||
}
|
||||
|
||||
let head = browser.contentDocument.getElementById("linkparent");
|
||||
let link = browser.contentDocument.createElement("link");
|
||||
link.rel = rel;
|
||||
link.href = href;
|
||||
link.type = type;
|
||||
link.title = title;
|
||||
head.appendChild(link);
|
||||
}
|
||||
|
||||
let feedDiscoveryTests = [
|
||||
{ text: "rel feed discovered" },
|
||||
{ rel: "ALTERNATE", text: "rel is case insensitive" },
|
||||
{ rel: "-alternate-", pass: false, text: "rel -alternate- not discovered" },
|
||||
{ rel: "foo bar baz alternate quux", text: "rel may contain additional rels separated by spaces" },
|
||||
{ href: "https://not.mozilla.com", text: "HTTPS ok" },
|
||||
{ href: "ftp://not.mozilla.com", text: "FTP ok" },
|
||||
{ href: "data:text/foo,foo", pass: false, text: "data URI not permitted" },
|
||||
{ href: "javascript:alert(0)", pass: false, text: "JS URI not permitted" },
|
||||
{ type: "application/rss+xml", text: "type can be RSS" },
|
||||
{ type: "aPPliCAtion/RSS+xml", text: "type is case insensitve" },
|
||||
{ type: " application/atom+xml ", text: "type may contain extra whitespace" },
|
||||
{ type: "application/atom+xml; charset=utf-8", text: "type may have optional parameters (RFC2046)" },
|
||||
{ type: "aapplication/atom+xml", pass: false, text: "type should not be loosely matched" },
|
||||
{ rel: "alternate alternate alternate", count: 1, text: "only one feed should be added" }
|
||||
];
|
||||
|
||||
function execute_feed_test(test) {
|
||||
if (browser.feeds) {
|
||||
let matchCount = (!("count" in test) || browser.feeds.length === test.count);
|
||||
let matchTitle = (test.title == browser.feeds[0].title);
|
||||
ok(matchCount && matchTitle, test.text);
|
||||
browser.feeds = null;
|
||||
} else {
|
||||
ok(!test.pass, test.text);
|
||||
}
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function prep_feed_test(test) {
|
||||
setHandlerFunc(execute_feed_test, test);
|
||||
|
||||
let rel = test.rel || "alternate";
|
||||
let href = test.href || "http://so.not.here.mozilla.com/feed.xml";
|
||||
let type = test.type || "application/atom+xml";
|
||||
let title = test.title;
|
||||
if (!("pass" in test)) {
|
||||
test.pass = true;
|
||||
}
|
||||
|
||||
let head = browser.contentDocument.getElementById("linkparent");
|
||||
let link = browser.contentDocument.createElement("link");
|
||||
link.rel = rel;
|
||||
link.href = href;
|
||||
link.type = type;
|
||||
link.title = title;
|
||||
head.appendChild(link);
|
||||
}
|
||||
|
||||
let searchTest;
|
||||
while ((searchTest = searchDiscoveryTests.shift())) {
|
||||
let title = searchTest.title || searchDiscoveryTests.length;
|
||||
searchTest.title = title;
|
||||
add_test(prep_search_test.bind(this, searchTest));
|
||||
}
|
||||
|
||||
let feedTest;
|
||||
while ((feedTest = feedDiscoveryTests.shift())) {
|
||||
let title = feedTest.title || feedDiscoveryTests.length;
|
||||
feedTest.title = title;
|
||||
add_test(prep_feed_test.bind(this, feedTest));
|
||||
}
|
||||
|
||||
run_next_test();
|
|
@ -365,10 +365,7 @@ public class BrowserToolbar extends GeckoRelativeLayout
|
|||
menu.findItem(R.id.add_to_launcher).setVisible(false);
|
||||
}
|
||||
|
||||
if (!tab.hasFeeds()) {
|
||||
menu.findItem(R.id.subscribe).setVisible(false);
|
||||
}
|
||||
|
||||
menu.findItem(R.id.subscribe).setVisible(tab.hasFeeds());
|
||||
menu.findItem(R.id.add_search_engine).setVisible(tab.hasOpenSearch());
|
||||
} else {
|
||||
// if there is no tab, remove anything tab dependent
|
||||
|
|
|
@ -3777,18 +3777,8 @@ Tab.prototype = {
|
|||
|
||||
// Clear page-specific opensearch engines and feeds for a new request.
|
||||
if (aStateFlags & Ci.nsIWebProgressListener.STATE_START && aRequest && aWebProgress.isTopLevel) {
|
||||
this.browser.engines = null;
|
||||
|
||||
// Send message to clear search engine option in context menu.
|
||||
let newEngineMessage = {
|
||||
type: "Link:OpenSearch",
|
||||
tabID: this.id,
|
||||
visible: false
|
||||
};
|
||||
|
||||
sendMessageToJava(newEngineMessage);
|
||||
|
||||
this.browser.feeds = null;
|
||||
this.browser.engines = null;
|
||||
this.browser.feeds = null;
|
||||
}
|
||||
|
||||
// true if the page loaded successfully (i.e., no 404s or other errors)
|
||||
|
|
Загрузка…
Ссылка в новой задаче