mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 08:15:31 +00:00
Bug 1479445: Update the validation of PageInfo to use validateItemProperties r=mak,Standard8
Changed the validation function for PageInfo to use a more general validateItemProperties. This changes the error message being thrown. Changed the respective test cases to accomodate the change. Differential Revision: https://phabricator.services.mozilla.com/D5831 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
3043f2a053
commit
2bd3beeba9
@ -298,8 +298,8 @@ add_task(async function test_add_url() {
|
||||
|
||||
let failTestData = [
|
||||
[{transition: "generated"}, "an invalid transition", "|generated| is not a supported transition for history"],
|
||||
[{visitTime: Date.now() + 1000000}, "a future date", "cannot be a future date"],
|
||||
[{url: "about.config"}, "an invalid url", "about.config is not a valid URL"],
|
||||
[{visitTime: Date.now() + 1000000}, "a future date", "Invalid value"],
|
||||
[{url: "about.config"}, "an invalid url", "Invalid value"],
|
||||
];
|
||||
|
||||
async function checkUrl(results) {
|
||||
|
@ -304,6 +304,88 @@ const SYNC_CHANGE_RECORD_VALIDATORS = Object.freeze({
|
||||
tombstone: simpleValidateFunc(v => v === true || v === false),
|
||||
synced: simpleValidateFunc(v => v === true || v === false),
|
||||
});
|
||||
/**
|
||||
* List PageInfo bookmark object validators.
|
||||
*/
|
||||
const PAGEINFO_VALIDATORS = Object.freeze({
|
||||
guid: BOOKMARK_VALIDATORS.guid,
|
||||
url: BOOKMARK_VALIDATORS.url,
|
||||
title: v => {
|
||||
if (v == null || v == undefined) {
|
||||
return undefined;
|
||||
} else if (typeof v === "string") {
|
||||
return v;
|
||||
}
|
||||
throw new TypeError(`title property of PageInfo object: ${v} must be a string if provided`);
|
||||
},
|
||||
previewImageURL: v => {
|
||||
if (!v) {
|
||||
return null;
|
||||
}
|
||||
return BOOKMARK_VALIDATORS.url(v);
|
||||
},
|
||||
description: v => {
|
||||
if (typeof v === "string" || v === null) {
|
||||
return v ? v.slice(0, DB_DESCRIPTION_LENGTH_MAX) : null;
|
||||
}
|
||||
throw new TypeError(`description property of pageInfo object: ${v} must be either a string or null if provided`);
|
||||
},
|
||||
annotations: v => {
|
||||
if (typeof v != "object" ||
|
||||
v.constructor.name != "Map") {
|
||||
throw new TypeError("annotations must be a Map");
|
||||
}
|
||||
|
||||
if (v.size == 0) {
|
||||
throw new TypeError("there must be at least one annotation");
|
||||
}
|
||||
|
||||
for (let [key, value] of v.entries()) {
|
||||
if (typeof key != "string") {
|
||||
throw new TypeError("all annotation keys must be strings");
|
||||
}
|
||||
if (typeof value != "string" &&
|
||||
typeof value != "number" &&
|
||||
typeof value != "boolean" &&
|
||||
value !== null &&
|
||||
value !== undefined) {
|
||||
throw new TypeError("all annotation values must be Boolean, Numbers or Strings");
|
||||
}
|
||||
}
|
||||
return v;
|
||||
},
|
||||
visits: v => {
|
||||
if (!Array.isArray(v) || !v.length) {
|
||||
throw new TypeError("PageInfo object must have an array of visits");
|
||||
}
|
||||
let visits = [];
|
||||
for (let inVisit of v) {
|
||||
let visit = {
|
||||
date: new Date(),
|
||||
transition: inVisit.transition || History.TRANSITIONS.LINK,
|
||||
};
|
||||
|
||||
if (!PlacesUtils.history.isValidTransition(visit.transition)) {
|
||||
throw new TypeError(`transition: ${visit.transition} is not a valid transition type`);
|
||||
}
|
||||
|
||||
if (inVisit.date) {
|
||||
PlacesUtils.history.ensureDate(inVisit.date);
|
||||
if (inVisit.date > (Date.now() + TIMERS_RESOLUTION_SKEW_MS)) {
|
||||
throw new TypeError(`date: ${inVisit.date} cannot be a future date`);
|
||||
}
|
||||
visit.date = inVisit.date;
|
||||
}
|
||||
|
||||
if (inVisit.referrer) {
|
||||
visit.referrer = PlacesUtils.normalizeToURLOrGUID(inVisit.referrer);
|
||||
}
|
||||
visits.push(visit);
|
||||
}
|
||||
return visits;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
var PlacesUtils = {
|
||||
// Place entries that are containers, e.g. bookmark folders or queries.
|
||||
@ -633,7 +715,7 @@ var PlacesUtils = {
|
||||
* @note any unknown properties are pass-through.
|
||||
*/
|
||||
validateItemProperties(name, validators, props, behavior = {}) {
|
||||
if (!props)
|
||||
if (typeof props != "object" || !props)
|
||||
throw new Error(`${name}: Input should be a valid object`);
|
||||
// Make a shallow copy of `props` to avoid mutating the original object
|
||||
// when filling in defaults.
|
||||
@ -1036,114 +1118,12 @@ var PlacesUtils = {
|
||||
* @return (PageInfo)
|
||||
*/
|
||||
validatePageInfo(pageInfo, validateVisits = true) {
|
||||
let info = {
|
||||
visits: [],
|
||||
};
|
||||
|
||||
if (typeof pageInfo != "object" || !pageInfo) {
|
||||
throw new TypeError("pageInfo must be an object");
|
||||
}
|
||||
|
||||
if (!pageInfo.url) {
|
||||
throw new TypeError("PageInfo object must have a url property");
|
||||
}
|
||||
|
||||
info.url = this.normalizeToURLOrGUID(pageInfo.url);
|
||||
|
||||
if (typeof pageInfo.guid === "string" && this.isValidGuid(pageInfo.guid)) {
|
||||
info.guid = pageInfo.guid;
|
||||
} else if (pageInfo.guid) {
|
||||
throw new TypeError(`guid property of PageInfo object: ${pageInfo.guid} is invalid`);
|
||||
}
|
||||
|
||||
if (typeof pageInfo.title === "string") {
|
||||
info.title = pageInfo.title;
|
||||
} else if (pageInfo.title != null && pageInfo.title != undefined) {
|
||||
throw new TypeError(`title property of PageInfo object: ${pageInfo.title} must be a string if provided`);
|
||||
}
|
||||
|
||||
if ("description" in pageInfo && (typeof pageInfo.description === "string" || pageInfo.description === null)) {
|
||||
info.description = pageInfo.description ? pageInfo.description.slice(0, DB_DESCRIPTION_LENGTH_MAX) : null;
|
||||
} else if (pageInfo.description !== undefined) {
|
||||
throw new TypeError(`description property of pageInfo object: ${pageInfo.description} must be either a string or null if provided`);
|
||||
}
|
||||
|
||||
if ("previewImageURL" in pageInfo) {
|
||||
let previewImageURL = pageInfo.previewImageURL;
|
||||
|
||||
if (!previewImageURL) {
|
||||
info.previewImageURL = null;
|
||||
} else if (typeof(previewImageURL) === "string" && previewImageURL.length <= DB_URL_LENGTH_MAX) {
|
||||
info.previewImageURL = new URL(previewImageURL);
|
||||
} else if (previewImageURL instanceof Ci.nsIURI && previewImageURL.spec.length <= DB_URL_LENGTH_MAX) {
|
||||
info.previewImageURL = new URL(previewImageURL.spec);
|
||||
} else if (previewImageURL instanceof URL && previewImageURL.href.length <= DB_URL_LENGTH_MAX) {
|
||||
info.previewImageURL = previewImageURL;
|
||||
} else {
|
||||
throw new TypeError("previewImageURL property of pageInfo object: ${previewImageURL} is invalid");
|
||||
}
|
||||
}
|
||||
|
||||
if (pageInfo.annotations) {
|
||||
if (typeof pageInfo.annotations != "object" ||
|
||||
pageInfo.annotations.constructor.name != "Map") {
|
||||
throw new TypeError("annotations must be a Map");
|
||||
}
|
||||
|
||||
if (pageInfo.annotations.size == 0) {
|
||||
throw new TypeError("there must be at least one annotation");
|
||||
}
|
||||
|
||||
for (let [key, value] of pageInfo.annotations.entries()) {
|
||||
if (typeof key != "string") {
|
||||
throw new TypeError("all annotation keys must be strings");
|
||||
}
|
||||
if (typeof value != "string" &&
|
||||
typeof value != "number" &&
|
||||
typeof value != "boolean" &&
|
||||
value !== null &&
|
||||
value !== undefined) {
|
||||
throw new TypeError("all annotation values must be Boolean, Numbers or Strings");
|
||||
}
|
||||
}
|
||||
|
||||
info.annotations = pageInfo.annotations;
|
||||
}
|
||||
|
||||
if (!validateVisits) {
|
||||
return info;
|
||||
}
|
||||
|
||||
if (!pageInfo.visits || !Array.isArray(pageInfo.visits) || !pageInfo.visits.length) {
|
||||
throw new TypeError("PageInfo object must have an array of visits");
|
||||
}
|
||||
|
||||
for (let inVisit of pageInfo.visits) {
|
||||
let visit = {
|
||||
date: new Date(),
|
||||
transition: inVisit.transition || History.TRANSITIONS.LINK,
|
||||
};
|
||||
|
||||
if (!PlacesUtils.history.isValidTransition(visit.transition)) {
|
||||
throw new TypeError(`transition: ${visit.transition} is not a valid transition type`);
|
||||
}
|
||||
|
||||
if (inVisit.date) {
|
||||
PlacesUtils.history.ensureDate(inVisit.date);
|
||||
if (inVisit.date > (Date.now() + TIMERS_RESOLUTION_SKEW_MS)) {
|
||||
throw new TypeError(`date: ${inVisit.date} cannot be a future date`);
|
||||
}
|
||||
visit.date = inVisit.date;
|
||||
}
|
||||
|
||||
if (inVisit.referrer) {
|
||||
visit.referrer = this.normalizeToURLOrGUID(inVisit.referrer);
|
||||
}
|
||||
info.visits.push(visit);
|
||||
}
|
||||
return info;
|
||||
return this.validateItemProperties("PageInfo", PAGEINFO_VALIDATORS, pageInfo,
|
||||
{ url: { requiredIf: b => { typeof b.guid != "string"; } },
|
||||
guid: { requiredIf: b => { typeof b.url != "string"; } },
|
||||
visits: { requiredIf: b => validateVisits },
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Normalize a key to either a string (if it is a valid GUID) or an
|
||||
* instance of `URL` (if it is a `URL`, `nsIURI`, or a string
|
||||
|
@ -10,38 +10,38 @@ add_task(async function test_insert_error_cases() {
|
||||
|
||||
Assert.throws(
|
||||
() => PlacesUtils.history.insert(),
|
||||
/TypeError: pageInfo must be an object/,
|
||||
"passing a null into History.insert should throw a TypeError"
|
||||
/Error: PageInfo: Input should be /,
|
||||
"passing a null into History.insert should throw an Error"
|
||||
);
|
||||
Assert.throws(
|
||||
() => PlacesUtils.history.insert(1),
|
||||
/TypeError: pageInfo must be an object/,
|
||||
"passing a non object into History.insert should throw a TypeError"
|
||||
/Error: PageInfo: Input should be/,
|
||||
"passing a non object into History.insert should throw an Error"
|
||||
);
|
||||
Assert.throws(
|
||||
() => PlacesUtils.history.insert({}),
|
||||
/TypeError: PageInfo object must have a url property/,
|
||||
"passing an object without a url to History.insert should throw a TypeError"
|
||||
/Error: PageInfo: The following properties were expected/,
|
||||
"passing an object without a url to History.insert should throw an Error"
|
||||
);
|
||||
Assert.throws(
|
||||
() => PlacesUtils.history.insert({url: 123}),
|
||||
/TypeError: Invalid url or guid: 123/,
|
||||
"passing an object with an invalid url to History.insert should throw a TypeError"
|
||||
/Error: PageInfo: Invalid value for property/,
|
||||
"passing an object with an invalid url to History.insert should throw an Error"
|
||||
);
|
||||
Assert.throws(
|
||||
() => PlacesUtils.history.insert({url: TEST_URL}),
|
||||
/TypeError: PageInfo object must have an array of visits/,
|
||||
"passing an object without a visits property to History.insert should throw a TypeError"
|
||||
/Error: PageInfo: The following properties were expected/,
|
||||
"passing an object without a visits property to History.insert should throw an Error"
|
||||
);
|
||||
Assert.throws(
|
||||
() => PlacesUtils.history.insert({url: TEST_URL, visits: 1}),
|
||||
/TypeError: PageInfo object must have an array of visits/,
|
||||
"passing an object with a non-array visits property to History.insert should throw a TypeError"
|
||||
/Error: PageInfo: Invalid value for property/,
|
||||
"passing an object with a non-array visits property to History.insert should throw an Error"
|
||||
);
|
||||
Assert.throws(
|
||||
() => PlacesUtils.history.insert({url: TEST_URL, visits: []}),
|
||||
/TypeError: PageInfo object must have an array of visits/,
|
||||
"passing an object with an empty array as the visits property to History.insert should throw a TypeError"
|
||||
/Error: PageInfo: Invalid value for property/,
|
||||
"passing an object with an empty array as the visits property to History.insert should throw an Error"
|
||||
);
|
||||
Assert.throws(
|
||||
() => PlacesUtils.history.insert({
|
||||
@ -52,8 +52,8 @@ add_task(async function test_insert_error_cases() {
|
||||
date: "a",
|
||||
},
|
||||
]}),
|
||||
/TypeError: Expected a Date, got a/,
|
||||
"passing a visit object with an invalid date to History.insert should throw a TypeError"
|
||||
/PageInfo: Invalid value for property/,
|
||||
"passing a visit object with an invalid date to History.insert should throw an Error"
|
||||
);
|
||||
Assert.throws(
|
||||
() => PlacesUtils.history.insert({
|
||||
@ -67,8 +67,8 @@ add_task(async function test_insert_error_cases() {
|
||||
date: "a",
|
||||
},
|
||||
]}),
|
||||
/TypeError: Expected a Date, got a/,
|
||||
"passing a second visit object with an invalid date to History.insert should throw a TypeError"
|
||||
/PageInfo: Invalid value for property/,
|
||||
"passing a second visit object with an invalid date to History.insert should throw an Error"
|
||||
);
|
||||
let futureDate = new Date();
|
||||
futureDate.setDate(futureDate.getDate() + 1000);
|
||||
@ -81,8 +81,8 @@ add_task(async function test_insert_error_cases() {
|
||||
date: futureDate,
|
||||
},
|
||||
]}),
|
||||
/cannot be a future date/,
|
||||
"passing a visit object with a future date to History.insert should throw a TypeError"
|
||||
/PageInfo: Invalid value for property/,
|
||||
"passing a visit object with a future date to History.insert should throw an Error"
|
||||
);
|
||||
Assert.throws(
|
||||
() => PlacesUtils.history.insert({
|
||||
@ -90,8 +90,8 @@ add_task(async function test_insert_error_cases() {
|
||||
visits: [
|
||||
{transition: "a"},
|
||||
]}),
|
||||
/TypeError: transition: a is not a valid transition type/,
|
||||
"passing a visit object with an invalid transition to History.insert should throw a TypeError"
|
||||
/PageInfo: Invalid value for property/,
|
||||
"passing a visit object with an invalid transition to History.insert should throw an Error"
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -25,8 +25,8 @@ add_task(async function test_error_cases() {
|
||||
);
|
||||
Assert.throws(
|
||||
() => PlacesUtils.history.insertMany([validPageInfo, {}]),
|
||||
/TypeError: PageInfo object must have a url property/,
|
||||
"passing a second invalid PageInfo object to History.insertMany should throw a TypeError"
|
||||
/Error: PageInfo: The following properties were expected/,
|
||||
"passing a second invalid PageInfo object to History.insertMany should throw an Error"
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -10,26 +10,26 @@
|
||||
add_task(async function test_error_cases() {
|
||||
Assert.throws(
|
||||
() => PlacesUtils.history.update("not an object"),
|
||||
/TypeError: pageInfo must be/,
|
||||
"passing a string as pageInfo should throw a TypeError"
|
||||
/Error: PageInfo: Input should be a valid object/,
|
||||
"passing a string as pageInfo should throw an Error"
|
||||
);
|
||||
Assert.throws(
|
||||
() => PlacesUtils.history.update(null),
|
||||
/TypeError: pageInfo must be/,
|
||||
"passing a null as pageInfo should throw a TypeError"
|
||||
/Error: PageInfo: Input should be/,
|
||||
"passing a null as pageInfo should throw an Error"
|
||||
);
|
||||
Assert.throws(
|
||||
() => PlacesUtils.history.update({url: "not a valid url string"}),
|
||||
/TypeError: not a valid url string is/,
|
||||
"passing an invalid url should throw a TypeError"
|
||||
/Error: PageInfo: Invalid value for property/,
|
||||
"passing an invalid url should throw an Error"
|
||||
);
|
||||
Assert.throws(
|
||||
() => PlacesUtils.history.update({
|
||||
url: "http://valid.uri.com",
|
||||
description: 123,
|
||||
}),
|
||||
/TypeError: description property of/,
|
||||
"passing a non-string description in pageInfo should throw a TypeError"
|
||||
/Error: PageInfo: Invalid value for property/,
|
||||
"passing a non-string description in pageInfo should throw an Error"
|
||||
);
|
||||
Assert.throws(
|
||||
() => PlacesUtils.history.update({
|
||||
@ -37,16 +37,16 @@ add_task(async function test_error_cases() {
|
||||
guid: "invalid guid",
|
||||
description: "Test description",
|
||||
}),
|
||||
/TypeError: guid property of/,
|
||||
"passing a invalid guid in pageInfo should throw a TypeError"
|
||||
/Error: PageInfo: Invalid value for property/,
|
||||
"passing a invalid guid in pageInfo should throw an Error"
|
||||
);
|
||||
Assert.throws(
|
||||
() => PlacesUtils.history.update({
|
||||
url: "http://valid.uri.com",
|
||||
previewImageURL: "not a valid url string",
|
||||
}),
|
||||
/TypeError: not a valid url string is/,
|
||||
"passing an invlid preview image url in pageInfo should throw a TypeError"
|
||||
/Error: PageInfo: Invalid value for property/,
|
||||
"passing an invlid preview image url in pageInfo should throw an Error"
|
||||
);
|
||||
Assert.throws(
|
||||
() => {
|
||||
@ -57,8 +57,8 @@ add_task(async function test_error_cases() {
|
||||
previewImageURL,
|
||||
});
|
||||
},
|
||||
/TypeError: previewImageURL property of/,
|
||||
"passing an oversized previewImageURL in pageInfo should throw a TypeError"
|
||||
/Error: PageInfo: Invalid value for property/,
|
||||
"passing an oversized previewImageURL in pageInfo should throw an Error"
|
||||
);
|
||||
Assert.throws(
|
||||
() => PlacesUtils.history.update({ url: "http://valid.uri.com" }),
|
||||
@ -67,37 +67,37 @@ add_task(async function test_error_cases() {
|
||||
);
|
||||
Assert.throws(
|
||||
() => PlacesUtils.history.update({ url: "http://valid.uri.com", annotations: "asd" }),
|
||||
/TypeError: annotations must be a Map/,
|
||||
"passing a pageInfo with incorrect annotations type should throw a TypeError"
|
||||
/Error: PageInfo: Invalid value for property/,
|
||||
"passing a pageInfo with incorrect annotations type should throw an Error"
|
||||
);
|
||||
Assert.throws(
|
||||
() => PlacesUtils.history.update({ url: "http://valid.uri.com", annotations: new Map() }),
|
||||
/TypeError: there must be at least one annotation/,
|
||||
"passing a pageInfo with an empty annotations type should throw a TypeError"
|
||||
/Error: PageInfo: Invalid value for property/,
|
||||
"passing a pageInfo with an empty annotations type should throw an Error"
|
||||
);
|
||||
Assert.throws(
|
||||
() => PlacesUtils.history.update({
|
||||
url: "http://valid.uri.com",
|
||||
annotations: new Map([[1234, "value"]]),
|
||||
}),
|
||||
/TypeError: all annotation keys must be strings/,
|
||||
"passing a pageInfo with an invalid key type should throw a TypeError"
|
||||
/Error: PageInfo: Invalid value for property/,
|
||||
"passing a pageInfo with an invalid key type should throw an Error"
|
||||
);
|
||||
Assert.throws(
|
||||
() => PlacesUtils.history.update({
|
||||
url: "http://valid.uri.com",
|
||||
annotations: new Map([["test", ["myarray"]]]),
|
||||
}),
|
||||
/TypeError: all annotation values must be Boolean, Numbers or Strings/,
|
||||
"passing a pageInfo with an invalid key type should throw a TypeError"
|
||||
/Error: PageInfo: Invalid value for property/,
|
||||
"passing a pageInfo with an invalid key type should throw an Error"
|
||||
);
|
||||
Assert.throws(
|
||||
() => PlacesUtils.history.update({
|
||||
url: "http://valid.uri.com",
|
||||
annotations: new Map([["test", {anno: "value"}]]),
|
||||
}),
|
||||
/TypeError: all annotation values must be Boolean, Numbers or Strings/,
|
||||
"passing a pageInfo with an invalid key type should throw a TypeError"
|
||||
/Error: PageInfo: Invalid value for property/,
|
||||
"passing a pageInfo with an invalid key type should throw an Error"
|
||||
);
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user