Bug 1251766 - Add new Date type to webextensions schemas r?kmag
MozReview-Commit-ID: EEX5FziiINo
--- a/toolkit/components/extensions/Schemas.jsm
+++ b/toolkit/components/extensions/Schemas.jsm
@@ -244,16 +244,31 @@ const FORMATS = {
new URL(string);
} catch (e) {
return FORMATS.relativeUrl(string, context);
}
}
throw new SyntaxError(`String ${JSON.stringify(string)} must be a relative URL`);
},
+
+ date(string, context) {
+ // A valid ISO 8601 timestamp.
+ const PATTERN = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d{3})?(Z|([-+]\d{2}:?\d{2})))?$/;
+ if (!PATTERN.test(string)) {
+ throw new Error(`Invalid date string ${string}`);
+ }
+ // Our pattern just checks the format, we could still have invalid
+ // values (e.g., month=99 or month=02 and day=31). Let the Date
+ // constructor do the dirty work of validating.
+ if (isNaN(new Date(string))) {
+ throw new Error(`Invalid date string ${string}`);
+ }
+ return string;
+ },
};
// Schema files contain namespaces, and each namespace contains types,
// properties, functions, and events. An Entry is a base class for
// types, properties, functions, and events.
class Entry {
constructor(schema = {}) {
/**
--- a/toolkit/components/extensions/schemas/extension_types.json
+++ b/toolkit/components/extensions/schemas/extension_types.json
@@ -54,12 +54,30 @@
"description": "The ID of the frame to inject the script into. This may not be used in combination with <code>allFrames</code>."
},
"runAt": {
"$ref": "RunAt",
"optional": true,
"description": "The soonest that the JavaScript or CSS will be injected into the tab. Defaults to \"document_idle\"."
}
}
+ },
+ {
+ "id": "Date",
+ "choices": [
+ {
+ "type": "string",
+ "format": "date"
+ },
+ {
+ "type": "integer",
+ "minimum": 0
+ },
+ {
+ "type": "object",
+ "isInstanceOf": "Date",
+ "additionalProperties": { "type": "any" }
+ }
+ ]
}
]
}
]
--- a/toolkit/components/extensions/test/xpcshell/test_ext_schemas.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_schemas.js
@@ -211,16 +211,30 @@ let json = [
relativeUrl: {type: "string", "format": "relativeUrl", "optional": true},
strictRelativeUrl: {type: "string", "format": "strictRelativeUrl", "optional": true},
},
},
],
},
{
+ name: "formatDate",
+ type: "function",
+ parameters: [
+ {
+ name: "arg",
+ type: "object",
+ properties: {
+ date: {type: "string", format: "date", optional: true},
+ },
+ },
+ ],
+ },
+
+ {
name: "deep",
type: "function",
parameters: [
{
name: "arg",
type: "object",
properties: {
foo: {
@@ -564,16 +578,53 @@ add_task(function* () {
}
for (let url of ["//foo.html", "http://foo/bar.html"]) {
Assert.throws(() => root.testing.format({strictRelativeUrl: url}),
/must be a relative URL/,
"should throw for non-relative URL");
}
+ const dates = [
+ "2016-03-04",
+ "2016-03-04T08:00:00Z",
+ "2016-03-04T08:00:00.000Z",
+ "2016-03-04T08:00:00-08:00",
+ "2016-03-04T08:00:00.000-08:00",
+ "2016-03-04T08:00:00+08:00",
+ "2016-03-04T08:00:00.000+08:00",
+ "2016-03-04T08:00:00+0800",
+ "2016-03-04T08:00:00-0800",
+ ];
+ dates.forEach(str => {
+ root.testing.formatDate({date: str});
+ verify("call", "testing", "formatDate", [{date: str}]);
+ });
+
+ // Make sure that a trivial change to a valid date invalidates it.
+ dates.forEach(str => {
+ Assert.throws(() => root.testing.formatDate({date: "0" + str}),
+ /Invalid date string/,
+ "should throw for invalid iso date string");
+ Assert.throws(() => root.testing.formatDate({date: str + "0"}),
+ /Invalid date string/,
+ "should throw for invalid iso date string");
+ });
+
+ const badDates = [
+ "I do not look anything like a date string",
+ "2016-99-99",
+ "2016-03-04T25:00:00Z",
+ ];
+ badDates.forEach(str => {
+ Assert.throws(() => root.testing.formatDate({date: str}),
+ /Invalid date string/,
+ "should throw for invalid iso date string");
+ });
+
root.testing.deep({foo: {bar: [{baz: {required: 12, optional: "42"}}]}});
verify("call", "testing", "deep", [{foo: {bar: [{baz: {required: 12, optional: "42"}}]}}]);
tallied = null;
Assert.throws(() => root.testing.deep({foo: {bar: [{baz: {optional: "42"}}]}}),
/Type error for parameter arg \(Error processing foo\.bar\.0\.baz: Property "required" is required\) for testing\.deep/,
"should throw with the correct object path");