зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1731940 - Web Manifest: implement id processing r=saschanaz
spec change https://github.com/w3c/manifest/pull/988 Differential Revision: https://phabricator.services.mozilla.com/D126331
This commit is contained in:
Родитель
362e26da3b
Коммит
fd25c8573e
|
@ -212,6 +212,8 @@ ServiceWorkerGraceTimeoutTermination=Terminating ServiceWorker for scope ‘%1$S
|
|||
# LOCALIZATION NOTE (ServiceWorkerNoFetchHandler): Do not translate "Fetch".
|
||||
ServiceWorkerNoFetchHandler=Fetch event handlers must be added during the worker script’s initial evaluation.
|
||||
ExecCommandCutCopyDeniedNotInputDriven=document.execCommand(‘cut’/‘copy’) was denied because it was not called from inside a short running user-generated event handler.
|
||||
ManifestIdIsInvalid=The id member did not resolve to a valid URL.
|
||||
ManifestIdNotSameOrigin=The id member must have the same origin as the start_url member.
|
||||
ManifestShouldBeObject=Manifest should be an object.
|
||||
ManifestScopeURLInvalid=The scope URL is invalid.
|
||||
ManifestScopeNotSameOrigin=The scope URL must be same origin as document.
|
||||
|
|
|
@ -124,6 +124,7 @@ var ManifestProcessor = {
|
|||
background_color: processBackgroundColorMember(),
|
||||
};
|
||||
processedManifest.scope = processScopeMember();
|
||||
processedManifest.id = processIdMember();
|
||||
if (checkConformance) {
|
||||
processedManifest.moz_validation = errors;
|
||||
processedManifest.moz_manifest_url = manifestURL.href;
|
||||
|
@ -258,10 +259,10 @@ var ManifestProcessor = {
|
|||
expectedType: "string",
|
||||
trim: false,
|
||||
};
|
||||
let result = new URL(docURL).href;
|
||||
const defaultStartURL = new URL(docURL).href;
|
||||
const value = extractor.extractValue(spec);
|
||||
if (value === undefined || value === "") {
|
||||
return result;
|
||||
return defaultStartURL;
|
||||
}
|
||||
let potentialResult;
|
||||
try {
|
||||
|
@ -269,17 +270,16 @@ var ManifestProcessor = {
|
|||
} catch (e) {
|
||||
const warn = domBundle.GetStringFromName("ManifestStartURLInvalid");
|
||||
errors.push({ warn });
|
||||
return result;
|
||||
return defaultStartURL;
|
||||
}
|
||||
if (potentialResult.origin !== docURL.origin) {
|
||||
const warn = domBundle.GetStringFromName(
|
||||
"ManifestStartURLShouldBeSameOrigin"
|
||||
);
|
||||
errors.push({ warn });
|
||||
} else {
|
||||
result = potentialResult.href;
|
||||
return defaultStartURL;
|
||||
}
|
||||
return result;
|
||||
return potentialResult.href;
|
||||
}
|
||||
|
||||
function processThemeColorMember() {
|
||||
|
@ -314,6 +314,42 @@ var ManifestProcessor = {
|
|||
};
|
||||
return extractor.extractLanguageValue(spec);
|
||||
}
|
||||
|
||||
function processIdMember() {
|
||||
// the start_url serves as the fallback, in case the id is not specified
|
||||
// or in error. A start_url is assured.
|
||||
const startURL = new URL(processedManifest.start_url);
|
||||
|
||||
const spec = {
|
||||
objectName: "manifest",
|
||||
object: rawManifest,
|
||||
property: "id",
|
||||
expectedType: "string",
|
||||
trim: false,
|
||||
};
|
||||
const extractedValue = extractor.extractValue(spec);
|
||||
|
||||
if (typeof extractedValue !== "string" || extractedValue === "") {
|
||||
return startURL.href;
|
||||
}
|
||||
|
||||
let appId;
|
||||
try {
|
||||
appId = new URL(extractedValue, startURL.origin);
|
||||
} catch {
|
||||
const warn = domBundle.GetStringFromName("ManifestIdIsInvalid");
|
||||
errors.push({ warn });
|
||||
return startURL.href;
|
||||
}
|
||||
|
||||
if (appId.origin !== startURL.origin) {
|
||||
const warn = domBundle.GetStringFromName("ManifestIdNotSameOrigin");
|
||||
errors.push({ warn });
|
||||
return startURL.href;
|
||||
}
|
||||
|
||||
return appId.href;
|
||||
}
|
||||
},
|
||||
};
|
||||
var EXPORTED_SYMBOLS = ["ManifestProcessor"];
|
||||
|
|
|
@ -13,6 +13,7 @@ support-files =
|
|||
[test_ManifestProcessor_dir.html]
|
||||
[test_ManifestProcessor_display.html]
|
||||
[test_ManifestProcessor_icons.html]
|
||||
[test_ManifestProcessor_id.html]
|
||||
[test_ManifestProcessor_JSON.html]
|
||||
[test_ManifestProcessor_lang.html]
|
||||
[test_ManifestProcessor_name_and_short_name.html]
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1731940
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1731940 - implement id member</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script src="common.js"></script>
|
||||
<script>
|
||||
/**
|
||||
* Manifest id member
|
||||
* https://w3c.github.io/manifest/#id-member
|
||||
**/
|
||||
for (const type of typeTests) {
|
||||
data.jsonText = JSON.stringify({
|
||||
id: type,
|
||||
});
|
||||
const result = processor.process(data);
|
||||
is(
|
||||
result.id.toString(),
|
||||
result.start_url.toString(),
|
||||
`Expect non-string id to fall back to start_url: ${typeof type}.`
|
||||
);
|
||||
}
|
||||
|
||||
// Invalid URLs
|
||||
const invalidURLs = [
|
||||
"https://foo:65536",
|
||||
"https://foo\u0000/",
|
||||
"//invalid:65555",
|
||||
"file:///passwords",
|
||||
"about:blank",
|
||||
"data:text/html,<html><script>alert('lol')<\/script></html>",
|
||||
];
|
||||
|
||||
for (const url of invalidURLs) {
|
||||
data.jsonText = JSON.stringify({
|
||||
id: url,
|
||||
});
|
||||
const result = processor.process(data);
|
||||
is(
|
||||
result.id.toString(),
|
||||
result.start_url.toString(),
|
||||
"Expect invalid id URL to fall back to start_url."
|
||||
);
|
||||
}
|
||||
|
||||
// Not same origin
|
||||
data.jsonText = JSON.stringify({
|
||||
id: "http://not-same-origin",
|
||||
});
|
||||
var result = processor.process(data);
|
||||
is(
|
||||
result.id.toString(),
|
||||
result.start_url,
|
||||
"Expect different origin id to fall back to start_url."
|
||||
);
|
||||
|
||||
// Empty string test
|
||||
data.jsonText = JSON.stringify({
|
||||
id: "",
|
||||
});
|
||||
result = processor.process(data);
|
||||
is(
|
||||
result.id.toString(),
|
||||
result.start_url.toString(),
|
||||
`Expect empty string for id to use start_url.`
|
||||
);
|
||||
|
||||
// Resolve URLs relative to the start_url's origin
|
||||
const URLs = [
|
||||
"path",
|
||||
"/path",
|
||||
"../../path",
|
||||
"./path",
|
||||
`${whiteSpace}path${whiteSpace}`,
|
||||
`${whiteSpace}/path`,
|
||||
`${whiteSpace}../../path`,
|
||||
`${whiteSpace}./path`,
|
||||
];
|
||||
|
||||
for (const url of URLs) {
|
||||
data.jsonText = JSON.stringify({
|
||||
id: url,
|
||||
start_url: "/path/some.html",
|
||||
});
|
||||
result = processor.process(data);
|
||||
const baseOrigin = new URL(result.start_url.toString()).origin;
|
||||
const expectedUrl = new URL(url, baseOrigin).toString();
|
||||
is(
|
||||
result.id.toString(),
|
||||
expectedUrl,
|
||||
"Expected id to be resolved relative to start_url's origin."
|
||||
);
|
||||
}
|
||||
|
||||
// Handles unicode encoded URLs
|
||||
const specialCases = [
|
||||
["😀", "%F0%9F%98%80"],
|
||||
[
|
||||
"this/is/ok?query_is_ok=😀#keep_hash",
|
||||
"this/is/ok?query_is_ok=%F0%9F%98%80#keep_hash",
|
||||
],
|
||||
];
|
||||
for (const [id, expected] of specialCases) {
|
||||
data.jsonText = JSON.stringify({
|
||||
id,
|
||||
start_url: "/my-app/",
|
||||
});
|
||||
result = processor.process(data);
|
||||
const baseOrigin = new URL(result.start_url.toString()).origin;
|
||||
const expectedUrl = new URL(expected, baseOrigin).toString();
|
||||
is(
|
||||
result.id.toString(),
|
||||
expectedUrl,
|
||||
`Expect id to be encoded/decoded per URL spec.`
|
||||
);
|
||||
}
|
||||
</script>
|
||||
</head>
|
|
@ -40,9 +40,11 @@ data.jsonText = JSON.stringify({
|
|||
result = processor.process(data);
|
||||
is(result.start_url.toString(), docURL.toString(), expected);
|
||||
|
||||
|
||||
// Resolve URLs relative to manfiest
|
||||
var URLs = ["path", "/path", "../../path",
|
||||
// Resolve URLs relative to manifest
|
||||
var URLs = [
|
||||
"path",
|
||||
"/path",
|
||||
"../../path",
|
||||
`${whiteSpace}path${whiteSpace}`,
|
||||
`${whiteSpace}/path`,
|
||||
`${whiteSpace}../../path`,
|
||||
|
@ -58,5 +60,24 @@ URLs.forEach((url) => {
|
|||
is(result.start_url.toString(), absURL, expected);
|
||||
});
|
||||
|
||||
</script>
|
||||
</head>
|
||||
// It retains the fragment
|
||||
var startURL = "./path?query=123#fragment";
|
||||
data.jsonText = JSON.stringify({
|
||||
start_url: startURL,
|
||||
});
|
||||
var absURL = new URL(startURL, manifestURL).href;
|
||||
result = processor.process(data);
|
||||
is(result.start_url.toString(), absURL, "Retains fragment");
|
||||
|
||||
// It retains the fragment on the document's location too.
|
||||
window.location = "#here";
|
||||
data.jsonText = JSON.stringify({});
|
||||
result = processor.process(data);
|
||||
is(
|
||||
window.location.href,
|
||||
result.start_url.toString(),
|
||||
`Retains the fragment of document's location`
|
||||
);
|
||||
</script>
|
||||
</head>
|
||||
</html>
|
||||
|
|
|
@ -112,6 +112,25 @@ const options = {...data, checkConformance: true } ;
|
|||
},
|
||||
warn: "icons item at index 1 includes repeated purpose(s): any maskable.",
|
||||
},
|
||||
// testing dom.properties: ManifestIdIsInvalid
|
||||
{
|
||||
func() {
|
||||
return (options.jsonText = JSON.stringify({
|
||||
id: "http://test:65536/foo",
|
||||
}));
|
||||
},
|
||||
warn: "The id member did not resolve to a valid URL.",
|
||||
},
|
||||
// testing dom.properties ManifestIdNotSameOrigin
|
||||
{
|
||||
func() {
|
||||
return (options.jsonText = JSON.stringify({
|
||||
id: "https://other.com",
|
||||
start_url: "/this/place"
|
||||
}));
|
||||
},
|
||||
warn: "The id member must have the same origin as the start_url member.",
|
||||
}
|
||||
].forEach((test, index) => {
|
||||
test.func();
|
||||
const result = processor.process(options);
|
||||
|
|
Загрузка…
Ссылка в новой задаче