forked from mirrors/gecko-dev
714 lines
18 KiB
JavaScript
714 lines
18 KiB
JavaScript
"use strict";
|
|
|
|
let json = [
|
|
{
|
|
namespace: "MV2",
|
|
max_manifest_version: 2,
|
|
|
|
properties: {
|
|
PROP1: { value: 20 },
|
|
},
|
|
},
|
|
{
|
|
namespace: "MV3",
|
|
min_manifest_version: 3,
|
|
properties: {
|
|
PROP1: { value: 20 },
|
|
},
|
|
},
|
|
{
|
|
namespace: "mixed",
|
|
|
|
properties: {
|
|
PROP_any: { value: 20 },
|
|
PROP_mv3: {
|
|
$ref: "submodule",
|
|
},
|
|
},
|
|
types: [
|
|
{
|
|
id: "manifestTest",
|
|
type: "object",
|
|
properties: {
|
|
// An example of extending the base type for permissions
|
|
permissions: {
|
|
type: "array",
|
|
items: {
|
|
$ref: "BaseType",
|
|
},
|
|
optional: true,
|
|
default: [],
|
|
},
|
|
// An example of differentiating versions of a manifest entry
|
|
multiple_choice: {
|
|
optional: true,
|
|
choices: [
|
|
{
|
|
max_manifest_version: 2,
|
|
type: "array",
|
|
items: {
|
|
type: "string",
|
|
},
|
|
},
|
|
{
|
|
min_manifest_version: 3,
|
|
type: "array",
|
|
items: {
|
|
type: "boolean",
|
|
},
|
|
},
|
|
{
|
|
type: "array",
|
|
items: {
|
|
type: "object",
|
|
properties: {
|
|
value: { type: "boolean" },
|
|
},
|
|
},
|
|
},
|
|
],
|
|
},
|
|
accepting_unrecognized_props: {
|
|
optional: true,
|
|
type: "object",
|
|
properties: {
|
|
mv2_only_prop: {
|
|
type: "string",
|
|
optional: true,
|
|
max_manifest_version: 2,
|
|
},
|
|
mv3_only_prop: {
|
|
type: "string",
|
|
optional: true,
|
|
min_manifest_version: 3,
|
|
},
|
|
mv2_only_prop_with_default: {
|
|
type: "string",
|
|
optional: true,
|
|
default: "only in MV2",
|
|
max_manifest_version: 2,
|
|
},
|
|
mv3_only_prop_with_default: {
|
|
type: "string",
|
|
optional: true,
|
|
default: "only in MV3",
|
|
min_manifest_version: 3,
|
|
},
|
|
},
|
|
additionalProperties: { $ref: "UnrecognizedProperty" },
|
|
},
|
|
},
|
|
},
|
|
{
|
|
id: "submodule",
|
|
type: "object",
|
|
min_manifest_version: 3,
|
|
functions: [
|
|
{
|
|
name: "sub_foo",
|
|
type: "function",
|
|
parameters: [],
|
|
returns: { type: "integer" },
|
|
},
|
|
{
|
|
name: "sub_no_match",
|
|
type: "function",
|
|
max_manifest_version: 2,
|
|
parameters: [],
|
|
returns: { type: "integer" },
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: "BaseType",
|
|
choices: [
|
|
{
|
|
type: "string",
|
|
enum: ["base"],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: "type_any",
|
|
type: "string",
|
|
enum: ["value1", "value2", "value3"],
|
|
},
|
|
{
|
|
id: "type_mv2",
|
|
max_manifest_version: 2,
|
|
type: "string",
|
|
enum: ["value1", "value2", "value3"],
|
|
},
|
|
{
|
|
id: "type_mv3",
|
|
min_manifest_version: 3,
|
|
type: "string",
|
|
enum: ["value1", "value2", "value3"],
|
|
},
|
|
{
|
|
id: "param_type_changed",
|
|
type: "array",
|
|
items: {
|
|
choices: [
|
|
{ max_manifest_version: 2, type: "string" },
|
|
{
|
|
min_manifest_version: 3,
|
|
type: "boolean",
|
|
},
|
|
],
|
|
},
|
|
},
|
|
{
|
|
id: "object_type_changed",
|
|
type: "object",
|
|
properties: {
|
|
prop_mv2: {
|
|
type: "string",
|
|
max_manifest_version: 2,
|
|
},
|
|
prop_mv3: {
|
|
type: "string",
|
|
min_manifest_version: 3,
|
|
},
|
|
prop_any: {
|
|
type: "string",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
id: "no_valid_choices",
|
|
type: "array",
|
|
items: {
|
|
choices: [
|
|
{ max_manifest_version: 1, type: "string" },
|
|
{
|
|
min_manifest_version: 4,
|
|
type: "boolean",
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
|
|
functions: [
|
|
{
|
|
name: "fun_param_type_versioned",
|
|
type: "function",
|
|
parameters: [{ name: "arg1", $ref: "param_type_changed" }],
|
|
},
|
|
{
|
|
name: "fun_mv2",
|
|
max_manifest_version: 2,
|
|
type: "function",
|
|
parameters: [
|
|
{ name: "arg1", type: "integer", optional: true, default: 99 },
|
|
{ name: "arg2", type: "boolean", optional: true },
|
|
],
|
|
},
|
|
{
|
|
name: "fun_mv3",
|
|
min_manifest_version: 3,
|
|
type: "function",
|
|
parameters: [
|
|
{ name: "arg1", type: "integer", optional: true, default: 99 },
|
|
{ name: "arg2", type: "boolean", optional: true },
|
|
],
|
|
},
|
|
{
|
|
name: "fun_param_change",
|
|
type: "function",
|
|
parameters: [{ name: "arg1", $ref: "object_type_changed" }],
|
|
},
|
|
{
|
|
name: "fun_no_valid_param",
|
|
type: "function",
|
|
parameters: [{ name: "arg1", $ref: "no_valid_choices" }],
|
|
},
|
|
],
|
|
events: [
|
|
{
|
|
name: "onEvent_any",
|
|
type: "function",
|
|
},
|
|
{
|
|
name: "onEvent_mv2",
|
|
max_manifest_version: 2,
|
|
type: "function",
|
|
},
|
|
{
|
|
name: "onEvent_mv3",
|
|
min_manifest_version: 3,
|
|
type: "function",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
namespace: "mixed",
|
|
types: [
|
|
{
|
|
$extend: "BaseType",
|
|
choices: [
|
|
{
|
|
min_manifest_version: 3,
|
|
type: "string",
|
|
enum: ["extended"],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
namespace: "mixed",
|
|
types: [
|
|
{
|
|
$extend: "manifestTest",
|
|
properties: {
|
|
versioned_extend: {
|
|
optional: true,
|
|
// just a simple type here
|
|
type: "string",
|
|
max_manifest_version: 2,
|
|
},
|
|
},
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
add_task(async function setup() {
|
|
let url = "data:," + JSON.stringify(json);
|
|
Schemas._rootSchema = null;
|
|
await Schemas.load(url);
|
|
|
|
// We want the actual errors thrown here, and not warnings recast as errors.
|
|
ExtensionTestUtils.failOnSchemaWarnings(false);
|
|
});
|
|
|
|
add_task(async function test_inject_V2() {
|
|
// Test injecting into a V2 context.
|
|
let wrapper = getContextWrapper(2);
|
|
|
|
let root = {};
|
|
Schemas.inject(root, wrapper);
|
|
|
|
// Test elements available to both
|
|
Assert.equal(root.mixed.type_any.VALUE1, "value1", "type_any exists");
|
|
Assert.equal(root.mixed.PROP_any, 20, "mixed value property");
|
|
|
|
// Test elements available to MV2
|
|
Assert.equal(root.MV2.PROP1, 20, "MV2 value property");
|
|
Assert.equal(root.mixed.type_mv2.VALUE2, "value2", "type_mv2 exists");
|
|
|
|
// Test MV3 elements not available
|
|
Assert.equal(root.MV3, undefined, "MV3 not injected");
|
|
Assert.ok(!("MV3" in root), "MV3 not enumerable");
|
|
Assert.equal(
|
|
root.mixed.PROP_mv3,
|
|
undefined,
|
|
"mixed submodule property does not exist"
|
|
);
|
|
Assert.ok(
|
|
!("PROP_mv3" in root.mixed),
|
|
"mixed submodule property not enumerable"
|
|
);
|
|
Assert.equal(root.mixed.type_mv3, undefined, "type_mv3 does not exist");
|
|
|
|
// Function tests
|
|
Assert.ok(
|
|
"fun_param_type_versioned" in root.mixed,
|
|
"fun_param_type_versioned exists"
|
|
);
|
|
Assert.ok(
|
|
!!root.mixed.fun_param_type_versioned,
|
|
"fun_param_type_versioned exists"
|
|
);
|
|
Assert.ok("fun_mv2" in root.mixed, "fun_mv2 exists");
|
|
Assert.ok(!!root.mixed.fun_mv2, "fun_mv2 exists");
|
|
Assert.ok(!("fun_mv3" in root.mixed), "fun_mv3 does not exist");
|
|
Assert.ok(!root.mixed.fun_mv3, "fun_mv3 does not exist");
|
|
|
|
// Event tests
|
|
Assert.ok("onEvent_any" in root.mixed, "onEvent_any exists");
|
|
Assert.ok(!!root.mixed.onEvent_any, "onEvent_any exists");
|
|
Assert.ok("onEvent_mv2" in root.mixed, "onEvent_mv2 exists");
|
|
Assert.ok(!!root.mixed.onEvent_mv2, "onEvent_mv2 exists");
|
|
Assert.ok(!("onEvent_mv3" in root.mixed), "onEvent_mv3 does not exist");
|
|
Assert.ok(!root.mixed.onEvent_mv3, "onEvent_mv3 does not exist");
|
|
|
|
// Function call tests
|
|
root.mixed.fun_param_type_versioned(["hello"]);
|
|
wrapper.verify("call", "mixed", "fun_param_type_versioned", [["hello"]]);
|
|
Assert.throws(
|
|
() => root.mixed.fun_param_type_versioned([true]),
|
|
/Expected string instead of true/,
|
|
"fun_param_type_versioned should throw for invalid type"
|
|
);
|
|
|
|
let propObj = { prop_any: "prop_any", prop_mv2: "prop_mv2" };
|
|
root.mixed.fun_param_change(propObj);
|
|
wrapper.verify("call", "mixed", "fun_param_change", [propObj]);
|
|
|
|
// Still throw same error as we did before we knew of the MV3 property.
|
|
Assert.throws(
|
|
() => root.mixed.fun_param_change({ prop_mv3: "prop_mv3", ...propObj }),
|
|
/Type error for parameter arg1 \(Unexpected property "prop_mv3"\)/,
|
|
"generic unexpected property message for MV3 property in MV2 extension"
|
|
);
|
|
|
|
// But print the more specific and descriptive warning message to console.
|
|
wrapper.checkErrors([
|
|
`Property "prop_mv3" is unsupported in Manifest Version 2`,
|
|
]);
|
|
|
|
Assert.throws(
|
|
() => root.mixed.fun_no_valid_param("anything"),
|
|
/Incorrect argument types for mixed.fun_no_valid_param/,
|
|
"fun_no_valid_param should throw for versioned type"
|
|
);
|
|
});
|
|
|
|
function normalizeTest(manifest, test, wrapper) {
|
|
let normalized = Schemas.normalize(manifest, "mixed.manifestTest", wrapper);
|
|
test(normalized);
|
|
// The test function should call wrapper.checkErrors if it expected errors.
|
|
// Here we call checkErrors again to ensure that there are not any unexpected
|
|
// errors left.
|
|
wrapper.checkErrors([]);
|
|
}
|
|
|
|
add_task(async function test_normalize_V2() {
|
|
let wrapper = getContextWrapper(2);
|
|
|
|
// Test normalize additions to the manifest structure
|
|
normalizeTest(
|
|
{
|
|
versioned_extend: "test",
|
|
},
|
|
normalized => {
|
|
Assert.equal(
|
|
normalized.value.versioned_extend,
|
|
"test",
|
|
"resources normalized"
|
|
);
|
|
},
|
|
wrapper
|
|
);
|
|
|
|
// Test normalizing baseType
|
|
normalizeTest(
|
|
{
|
|
permissions: ["base"],
|
|
},
|
|
normalized => {
|
|
Assert.equal(
|
|
normalized.value.permissions[0],
|
|
"base",
|
|
"resources normalized"
|
|
);
|
|
},
|
|
wrapper
|
|
);
|
|
|
|
normalizeTest(
|
|
{
|
|
permissions: ["extended"],
|
|
},
|
|
normalized => {
|
|
Assert.ok(
|
|
normalized.error.startsWith("Error processing permissions.0"),
|
|
`manifest normalized error ${normalized.error}`
|
|
);
|
|
},
|
|
wrapper
|
|
);
|
|
|
|
// Test normalizing a value
|
|
normalizeTest(
|
|
{
|
|
multiple_choice: ["foo.html"],
|
|
},
|
|
normalized => {
|
|
Assert.equal(
|
|
normalized.value.multiple_choice[0],
|
|
"foo.html",
|
|
"resources normalized"
|
|
);
|
|
},
|
|
wrapper
|
|
);
|
|
|
|
normalizeTest(
|
|
{
|
|
multiple_choice: [true],
|
|
},
|
|
normalized => {
|
|
Assert.ok(
|
|
normalized.error.startsWith("Error processing multiple_choice"),
|
|
"manifest error"
|
|
);
|
|
},
|
|
wrapper
|
|
);
|
|
|
|
normalizeTest(
|
|
{
|
|
multiple_choice: [
|
|
{
|
|
value: true,
|
|
},
|
|
],
|
|
},
|
|
normalized => {
|
|
Assert.ok(
|
|
normalized.value.multiple_choice[0].value,
|
|
"resources normalized"
|
|
);
|
|
},
|
|
wrapper
|
|
);
|
|
|
|
// Tests that object definitions including additionalProperties can
|
|
// successfully accept objects from another manifest version, while ignoring
|
|
// the actual value from the non-matching manifest value.
|
|
normalizeTest(
|
|
{
|
|
accepting_unrecognized_props: {
|
|
mv2_only_prop: "mv2 here",
|
|
mv3_only_prop: "mv3 here",
|
|
},
|
|
},
|
|
normalized => {
|
|
equal(normalized.error, undefined, "no normalization error");
|
|
Assert.deepEqual(
|
|
normalized.value.accepting_unrecognized_props,
|
|
{
|
|
mv2_only_prop: "mv2 here",
|
|
mv2_only_prop_with_default: "only in MV2",
|
|
},
|
|
"Normalized object for MV2, without MV3-specific props"
|
|
);
|
|
wrapper.checkErrors([
|
|
`Property "mv3_only_prop" is unsupported in Manifest Version 2`,
|
|
]);
|
|
},
|
|
wrapper
|
|
);
|
|
});
|
|
|
|
add_task(async function test_inject_V3() {
|
|
// Test injecting into a V3 context.
|
|
let wrapper = getContextWrapper(3);
|
|
|
|
let root = {};
|
|
Schemas.inject(root, wrapper);
|
|
|
|
// Test elements available to both
|
|
Assert.equal(root.mixed.type_any.VALUE1, "value1", "type_any exists");
|
|
Assert.equal(root.mixed.PROP_any, 20, "mixed value property");
|
|
|
|
// Test elements available to MV2
|
|
Assert.equal(root.MV2, undefined, "MV2 value property");
|
|
Assert.ok(!("MV2" in root), "MV2 not enumerable");
|
|
Assert.equal(root.mixed.type_mv2, undefined, "type_mv2 does not exist");
|
|
Assert.ok(!("type_mv2" in root.mixed), "type_mv2 not enumerable");
|
|
|
|
// Test MV3 elements not available
|
|
Assert.equal(root.MV3.PROP1, 20, "MV3 injected");
|
|
Assert.ok(!!root.mixed.PROP_mv3, "mixed submodule property exists");
|
|
Assert.equal(root.mixed.type_mv3.VALUE3, "value3", "type_mv3 exists");
|
|
|
|
// Versioned submodule
|
|
Assert.ok(!!root.mixed.PROP_mv3.sub_foo, "mixed submodule sub_foo exists");
|
|
Assert.ok(
|
|
!root.mixed.PROP_mv3.sub_no_match,
|
|
"mixed submodule sub_no_match does not exist"
|
|
);
|
|
Assert.ok(
|
|
!("sub_no_match" in root.mixed.PROP_mv3),
|
|
"mixed submodule sub_no_match is not enumerable"
|
|
);
|
|
|
|
// Function tests
|
|
Assert.ok(
|
|
"fun_param_type_versioned" in root.mixed,
|
|
"fun_param_type_versioned exists"
|
|
);
|
|
Assert.ok(
|
|
!!root.mixed.fun_param_type_versioned,
|
|
"fun_param_type_versioned exists"
|
|
);
|
|
Assert.ok(!("fun_mv2" in root.mixed), "fun_mv2 does not exist");
|
|
Assert.ok(!root.mixed.fun_mv2, "fun_mv2 does not exist");
|
|
Assert.ok("fun_mv3" in root.mixed, "fun_mv3 exists");
|
|
Assert.ok(!!root.mixed.fun_mv3, "fun_mv3 exists");
|
|
|
|
// Event tests
|
|
Assert.ok("onEvent_any" in root.mixed, "onEvent_any exists");
|
|
Assert.ok(!!root.mixed.onEvent_any, "onEvent_any exists");
|
|
Assert.ok(!("onEvent_mv2" in root.mixed), "onEvent_mv2 not enumerable");
|
|
Assert.ok(!root.mixed.onEvent_mv2, "onEvent_mv2 does not exist");
|
|
Assert.ok("onEvent_mv3" in root.mixed, "onEvent_mv3 exists");
|
|
Assert.ok(!!root.mixed.onEvent_mv3, "onEvent_mv3 exists");
|
|
|
|
// Function call tests
|
|
root.mixed.fun_param_type_versioned([true]);
|
|
wrapper.verify("call", "mixed", "fun_param_type_versioned", [[true]]);
|
|
Assert.throws(
|
|
() => root.mixed.fun_param_type_versioned(["hello"]),
|
|
/Expected boolean instead of "hello"/,
|
|
"should throw for invalid type"
|
|
);
|
|
|
|
let propObj = { prop_any: "prop_any", prop_mv3: "prop_mv3" };
|
|
root.mixed.fun_param_change(propObj);
|
|
wrapper.verify("call", "mixed", "fun_param_change", [propObj]);
|
|
Assert.throws(
|
|
() => root.mixed.fun_param_change({ prop_mv2: "prop_mv2", ...propObj }),
|
|
/Unexpected property "prop_mv2"/,
|
|
"should throw for versioned type"
|
|
);
|
|
wrapper.checkErrors([
|
|
`Property "prop_mv2" is unsupported in Manifest Version 3`,
|
|
]);
|
|
|
|
root.mixed.PROP_mv3.sub_foo();
|
|
wrapper.verify("call", "mixed.PROP_mv3", "sub_foo", []);
|
|
Assert.throws(
|
|
() => root.mixed.PROP_mv3.sub_no_match(),
|
|
/TypeError: root.mixed.PROP_mv3.sub_no_match is not a function/,
|
|
"sub_no_match should throw"
|
|
);
|
|
});
|
|
|
|
add_task(async function test_normalize_V3() {
|
|
let wrapper = getContextWrapper(3);
|
|
|
|
// Test normalize additions to the manifest structure
|
|
normalizeTest(
|
|
{
|
|
versioned_extend: "test",
|
|
},
|
|
normalized => {
|
|
Assert.equal(
|
|
normalized.error,
|
|
`Unexpected property "versioned_extend"`,
|
|
"expected manifest error"
|
|
);
|
|
wrapper.checkErrors([
|
|
`Property "versioned_extend" is unsupported in Manifest Version 3`,
|
|
]);
|
|
},
|
|
wrapper
|
|
);
|
|
|
|
// Test normalizing baseType
|
|
normalizeTest(
|
|
{
|
|
permissions: ["base"],
|
|
},
|
|
normalized => {
|
|
Assert.equal(
|
|
normalized.value.permissions[0],
|
|
"base",
|
|
"resources normalized"
|
|
);
|
|
},
|
|
wrapper
|
|
);
|
|
|
|
normalizeTest(
|
|
{
|
|
permissions: ["extended"],
|
|
},
|
|
normalized => {
|
|
Assert.equal(
|
|
normalized.value.permissions[0],
|
|
"extended",
|
|
"resources normalized"
|
|
);
|
|
},
|
|
wrapper
|
|
);
|
|
|
|
// Test normalizing a value
|
|
normalizeTest(
|
|
{
|
|
multiple_choice: ["foo.html"],
|
|
},
|
|
normalized => {
|
|
Assert.ok(
|
|
normalized.error.startsWith("Error processing multiple_choice"),
|
|
"manifest error"
|
|
);
|
|
},
|
|
wrapper
|
|
);
|
|
|
|
normalizeTest(
|
|
{
|
|
multiple_choice: [true],
|
|
},
|
|
normalized => {
|
|
Assert.equal(
|
|
normalized.value.multiple_choice[0],
|
|
true,
|
|
"resources normalized"
|
|
);
|
|
},
|
|
wrapper
|
|
);
|
|
|
|
normalizeTest(
|
|
{
|
|
multiple_choice: [
|
|
{
|
|
value: true,
|
|
},
|
|
],
|
|
},
|
|
normalized => {
|
|
Assert.ok(
|
|
normalized.value.multiple_choice[0].value,
|
|
"resources normalized"
|
|
);
|
|
},
|
|
wrapper
|
|
);
|
|
|
|
wrapper.tallied = null;
|
|
|
|
normalizeTest(
|
|
{},
|
|
normalized => {
|
|
ok(!normalized.error, "manifest normalized");
|
|
},
|
|
wrapper
|
|
);
|
|
|
|
// Tests that object definitions including additionalProperties can
|
|
// successfully accept objects from another manifest version, while ignoring
|
|
// the actual value from the non-matching manifest value.
|
|
normalizeTest(
|
|
{
|
|
accepting_unrecognized_props: {
|
|
mv2_only_prop: "mv2 here",
|
|
mv3_only_prop: "mv3 here",
|
|
},
|
|
},
|
|
normalized => {
|
|
equal(normalized.error, undefined, "no normalization error");
|
|
Assert.deepEqual(
|
|
normalized.value.accepting_unrecognized_props,
|
|
{
|
|
mv3_only_prop: "mv3 here",
|
|
mv3_only_prop_with_default: "only in MV3",
|
|
},
|
|
"Normalized object for MV3, without MV2-specific props"
|
|
);
|
|
wrapper.checkErrors([
|
|
`Property "mv2_only_prop" is unsupported in Manifest Version 3`,
|
|
]);
|
|
},
|
|
wrapper
|
|
);
|
|
});
|