2014-10-31 21:24:47 +03:00
|
|
|
/* 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
|
2015-03-26 18:53:51 +03:00
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2015-05-19 15:20:00 +03:00
|
|
|
/*
|
2014-10-31 21:24:47 +03:00
|
|
|
* ManifestProcessor
|
|
|
|
* Implementation of processing algorithms from:
|
|
|
|
* http://www.w3.org/2008/webapps/manifest/
|
|
|
|
*
|
|
|
|
* Creates manifest processor that lets you process a JSON file
|
|
|
|
* or individual parts of a manifest object. A manifest is just a
|
|
|
|
* standard JS object that has been cleaned up.
|
|
|
|
*
|
2015-04-02 18:22:00 +03:00
|
|
|
* .process({jsonText,manifestURL,docURL});
|
2014-10-31 21:24:47 +03:00
|
|
|
*
|
2015-05-27 00:04:59 +03:00
|
|
|
* Depends on ImageObjectProcessor to process things like
|
2015-05-19 15:20:00 +03:00
|
|
|
* icons and splash_screens.
|
|
|
|
*
|
2014-10-31 21:24:47 +03:00
|
|
|
* TODO: The constructor should accept the UA's supported orientations.
|
|
|
|
* TODO: The constructor should accept the UA's supported display modes.
|
2015-04-02 18:22:00 +03:00
|
|
|
* TODO: hook up developer tools to console. (1086997).
|
2014-10-31 21:24:47 +03:00
|
|
|
*/
|
2018-12-18 23:38:43 +03:00
|
|
|
/* globals Components, ValueExtractor, ImageObjectProcessor, ConsoleAPI*/
|
|
|
|
"use strict";
|
2018-12-18 23:39:42 +03:00
|
|
|
|
2019-07-05 11:44:55 +03:00
|
|
|
const { XPCOMUtils } = ChromeUtils.import(
|
|
|
|
"resource://gre/modules/XPCOMUtils.jsm"
|
|
|
|
);
|
2018-12-18 23:38:43 +03:00
|
|
|
XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
|
2019-07-05 11:44:55 +03:00
|
|
|
const displayModes = new Set([
|
|
|
|
"fullscreen",
|
|
|
|
"standalone",
|
|
|
|
"minimal-ui",
|
2018-12-18 23:38:43 +03:00
|
|
|
"browser",
|
2014-10-31 21:24:47 +03:00
|
|
|
]);
|
2019-07-05 11:44:55 +03:00
|
|
|
const orientationTypes = new Set([
|
|
|
|
"any",
|
|
|
|
"natural",
|
|
|
|
"landscape",
|
|
|
|
"portrait",
|
|
|
|
"portrait-primary",
|
|
|
|
"portrait-secondary",
|
|
|
|
"landscape-primary",
|
2018-12-18 23:38:43 +03:00
|
|
|
"landscape-secondary",
|
2014-10-31 21:24:47 +03:00
|
|
|
]);
|
2018-12-18 23:38:43 +03:00
|
|
|
const textDirections = new Set(["ltr", "rtl", "auto"]);
|
2016-04-13 22:55:00 +03:00
|
|
|
|
2019-07-05 11:44:55 +03:00
|
|
|
const { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm");
|
|
|
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
2015-05-27 00:04:59 +03:00
|
|
|
// ValueExtractor is used by the various processors to get values
|
|
|
|
// from the manifest and to report errors.
|
2019-07-05 11:44:55 +03:00
|
|
|
const { ValueExtractor } = ChromeUtils.import(
|
|
|
|
"resource://gre/modules/ValueExtractor.jsm"
|
|
|
|
);
|
2015-05-27 00:04:59 +03:00
|
|
|
// ImageObjectProcessor is used to process things like icons and images
|
2019-07-05 11:44:55 +03:00
|
|
|
const { ImageObjectProcessor } = ChromeUtils.import(
|
|
|
|
"resource://gre/modules/ImageObjectProcessor.jsm"
|
|
|
|
);
|
2014-10-31 21:24:47 +03:00
|
|
|
|
2019-07-05 11:44:55 +03:00
|
|
|
const domBundle = Services.strings.createBundle(
|
|
|
|
"chrome://global/locale/dom/dom.properties"
|
|
|
|
);
|
2019-04-10 15:46:41 +03:00
|
|
|
|
2019-07-05 11:44:55 +03:00
|
|
|
var ManifestProcessor = {
|
|
|
|
// jshint ignore:line
|
2015-07-30 18:56:12 +03:00
|
|
|
get defaultDisplayMode() {
|
2018-12-18 23:38:43 +03:00
|
|
|
return "browser";
|
2015-07-29 17:58:00 +03:00
|
|
|
},
|
2015-07-30 18:56:12 +03:00
|
|
|
get displayModes() {
|
|
|
|
return displayModes;
|
|
|
|
},
|
|
|
|
get orientationTypes() {
|
|
|
|
return orientationTypes;
|
2015-04-02 18:22:00 +03:00
|
|
|
},
|
2016-04-13 22:55:00 +03:00
|
|
|
get textDirections() {
|
|
|
|
return textDirections;
|
|
|
|
},
|
2015-05-20 17:58:00 +03:00
|
|
|
// process() method processes JSON text into a clean manifest
|
2015-04-02 18:22:00 +03:00
|
|
|
// that conforms with the W3C specification. Takes an object
|
|
|
|
// expecting the following dictionary items:
|
2015-05-27 00:04:59 +03:00
|
|
|
// * jsonText: the JSON string to be processed.
|
|
|
|
// * manifestURL: the URL of the manifest, to resolve URLs.
|
|
|
|
// * docURL: the URL of the owner doc, for security checks
|
2019-04-10 15:46:41 +03:00
|
|
|
process(aOptions) {
|
2019-07-05 11:44:55 +03:00
|
|
|
const { jsonText, manifestURL: aManifestURL, docURL: aDocURL } = aOptions;
|
2015-04-02 18:22:00 +03:00
|
|
|
const console = new ConsoleAPI({
|
2018-12-18 23:38:43 +03:00
|
|
|
prefix: "Web Manifest",
|
2015-04-02 18:22:00 +03:00
|
|
|
});
|
|
|
|
let rawManifest = {};
|
|
|
|
try {
|
2015-05-27 00:04:59 +03:00
|
|
|
rawManifest = JSON.parse(jsonText);
|
2015-04-02 18:22:00 +03:00
|
|
|
} catch (e) {}
|
2019-04-10 15:46:41 +03:00
|
|
|
if (rawManifest === null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
if (typeof rawManifest !== "object") {
|
2018-12-18 23:38:43 +03:00
|
|
|
console.warn(domBundle.GetStringFromName("ManifestShouldBeObject"));
|
2015-04-02 18:22:00 +03:00
|
|
|
rawManifest = {};
|
|
|
|
}
|
2019-04-10 15:46:41 +03:00
|
|
|
const manifestURL = new URL(aManifestURL);
|
|
|
|
const docURL = new URL(aDocURL);
|
2016-02-03 03:47:51 +03:00
|
|
|
const extractor = new ValueExtractor(console, domBundle);
|
2015-05-27 00:04:59 +03:00
|
|
|
const imgObjProcessor = new ImageObjectProcessor(console, extractor);
|
2015-04-02 18:22:00 +03:00
|
|
|
const processedManifest = {
|
2019-07-05 11:44:55 +03:00
|
|
|
dir: processDirMember.call(this),
|
|
|
|
lang: processLangMember(),
|
|
|
|
start_url: processStartURLMember(),
|
|
|
|
display: processDisplayMember.call(this),
|
|
|
|
orientation: processOrientationMember.call(this),
|
|
|
|
name: processNameMember(),
|
|
|
|
icons: imgObjProcessor.process(rawManifest, manifestURL, "icons"),
|
|
|
|
short_name: processShortNameMember(),
|
|
|
|
theme_color: processThemeColorMember(),
|
|
|
|
background_color: processBackgroundColorMember(),
|
2015-03-26 18:53:51 +03:00
|
|
|
};
|
2015-05-27 00:04:59 +03:00
|
|
|
processedManifest.scope = processScopeMember();
|
2015-04-02 18:22:00 +03:00
|
|
|
return processedManifest;
|
2015-01-16 03:46:00 +03:00
|
|
|
|
2016-04-13 22:55:00 +03:00
|
|
|
function processDirMember() {
|
|
|
|
const spec = {
|
2018-12-18 23:38:43 +03:00
|
|
|
objectName: "manifest",
|
2016-04-13 22:55:00 +03:00
|
|
|
object: rawManifest,
|
2018-12-18 23:38:43 +03:00
|
|
|
property: "dir",
|
|
|
|
expectedType: "string",
|
2016-04-13 22:55:00 +03:00
|
|
|
trim: true,
|
|
|
|
};
|
|
|
|
const value = extractor.extractValue(spec);
|
|
|
|
if (this.textDirections.has(value)) {
|
|
|
|
return value;
|
|
|
|
}
|
2018-12-18 23:38:43 +03:00
|
|
|
return "auto";
|
2016-04-13 22:55:00 +03:00
|
|
|
}
|
|
|
|
|
2015-05-27 00:04:59 +03:00
|
|
|
function processNameMember() {
|
2015-04-02 18:22:00 +03:00
|
|
|
const spec = {
|
2018-12-18 23:38:43 +03:00
|
|
|
objectName: "manifest",
|
2015-05-27 00:04:59 +03:00
|
|
|
object: rawManifest,
|
2018-12-18 23:38:43 +03:00
|
|
|
property: "name",
|
|
|
|
expectedType: "string",
|
|
|
|
trim: true,
|
2015-04-02 18:22:00 +03:00
|
|
|
};
|
2015-05-20 17:58:00 +03:00
|
|
|
return extractor.extractValue(spec);
|
2015-01-16 03:46:00 +03:00
|
|
|
}
|
2014-10-31 21:24:47 +03:00
|
|
|
|
2015-05-27 00:04:59 +03:00
|
|
|
function processShortNameMember() {
|
2015-04-02 18:22:00 +03:00
|
|
|
const spec = {
|
2018-12-18 23:38:43 +03:00
|
|
|
objectName: "manifest",
|
2015-05-27 00:04:59 +03:00
|
|
|
object: rawManifest,
|
2018-12-18 23:38:43 +03:00
|
|
|
property: "short_name",
|
|
|
|
expectedType: "string",
|
|
|
|
trim: true,
|
2015-04-02 18:22:00 +03:00
|
|
|
};
|
2015-05-20 17:58:00 +03:00
|
|
|
return extractor.extractValue(spec);
|
2015-03-19 13:04:00 +03:00
|
|
|
}
|
2014-10-31 21:24:47 +03:00
|
|
|
|
2015-05-27 00:04:59 +03:00
|
|
|
function processOrientationMember() {
|
2015-04-02 18:22:00 +03:00
|
|
|
const spec = {
|
2018-12-18 23:38:43 +03:00
|
|
|
objectName: "manifest",
|
2015-05-27 00:04:59 +03:00
|
|
|
object: rawManifest,
|
2018-12-18 23:38:43 +03:00
|
|
|
property: "orientation",
|
|
|
|
expectedType: "string",
|
|
|
|
trim: true,
|
2015-04-02 18:22:00 +03:00
|
|
|
};
|
2015-05-20 17:58:00 +03:00
|
|
|
const value = extractor.extractValue(spec);
|
2019-07-05 11:44:55 +03:00
|
|
|
if (
|
|
|
|
value &&
|
|
|
|
typeof value === "string" &&
|
|
|
|
this.orientationTypes.has(value.toLowerCase())
|
|
|
|
) {
|
2016-05-26 01:44:00 +03:00
|
|
|
return value.toLowerCase();
|
2015-04-02 18:22:00 +03:00
|
|
|
}
|
2016-05-26 01:35:00 +03:00
|
|
|
return undefined;
|
2014-10-31 21:24:47 +03:00
|
|
|
}
|
|
|
|
|
2015-05-27 00:04:59 +03:00
|
|
|
function processDisplayMember() {
|
2015-04-02 18:22:00 +03:00
|
|
|
const spec = {
|
2018-12-18 23:38:43 +03:00
|
|
|
objectName: "manifest",
|
2015-05-27 00:04:59 +03:00
|
|
|
object: rawManifest,
|
2018-12-18 23:38:43 +03:00
|
|
|
property: "display",
|
|
|
|
expectedType: "string",
|
|
|
|
trim: true,
|
2015-04-02 18:22:00 +03:00
|
|
|
};
|
2015-05-20 17:58:00 +03:00
|
|
|
const value = extractor.extractValue(spec);
|
2019-07-05 11:44:55 +03:00
|
|
|
if (
|
|
|
|
value &&
|
|
|
|
typeof value === "string" &&
|
|
|
|
displayModes.has(value.toLowerCase())
|
|
|
|
) {
|
2016-05-26 01:44:00 +03:00
|
|
|
return value.toLowerCase();
|
2015-04-02 18:22:00 +03:00
|
|
|
}
|
2015-07-30 18:56:12 +03:00
|
|
|
return this.defaultDisplayMode;
|
2015-03-26 18:53:51 +03:00
|
|
|
}
|
2015-03-19 13:04:00 +03:00
|
|
|
|
2015-05-27 00:04:59 +03:00
|
|
|
function processScopeMember() {
|
2015-04-02 18:22:00 +03:00
|
|
|
const spec = {
|
2018-12-18 23:38:43 +03:00
|
|
|
objectName: "manifest",
|
2015-05-27 00:04:59 +03:00
|
|
|
object: rawManifest,
|
2018-12-18 23:38:43 +03:00
|
|
|
property: "scope",
|
|
|
|
expectedType: "string",
|
|
|
|
trim: false,
|
2015-04-02 18:22:00 +03:00
|
|
|
};
|
|
|
|
let scopeURL;
|
2015-05-27 00:04:59 +03:00
|
|
|
const startURL = new URL(processedManifest.start_url);
|
2015-05-20 17:58:00 +03:00
|
|
|
const value = extractor.extractValue(spec);
|
2018-12-18 23:38:43 +03:00
|
|
|
if (value === undefined || value === "") {
|
2015-04-14 17:00:00 +03:00
|
|
|
return undefined;
|
|
|
|
}
|
2015-04-02 18:22:00 +03:00
|
|
|
try {
|
2015-05-27 00:04:59 +03:00
|
|
|
scopeURL = new URL(value, manifestURL);
|
2015-04-02 18:22:00 +03:00
|
|
|
} catch (e) {
|
2018-12-18 23:38:43 +03:00
|
|
|
console.warn(domBundle.GetStringFromName("ManifestScopeURLInvalid"));
|
2015-04-02 18:22:00 +03:00
|
|
|
return undefined;
|
|
|
|
}
|
2015-05-27 00:04:59 +03:00
|
|
|
if (scopeURL.origin !== docURL.origin) {
|
2018-12-18 23:38:43 +03:00
|
|
|
console.warn(domBundle.GetStringFromName("ManifestScopeNotSameOrigin"));
|
2015-04-02 18:22:00 +03:00
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
// If start URL is not within scope of scope URL:
|
2015-05-27 00:04:59 +03:00
|
|
|
let isSameOrigin = startURL && startURL.origin !== scopeURL.origin;
|
|
|
|
if (isSameOrigin || !startURL.pathname.startsWith(scopeURL.pathname)) {
|
2019-07-05 11:44:55 +03:00
|
|
|
console.warn(
|
|
|
|
domBundle.GetStringFromName("ManifestStartURLOutsideScope")
|
|
|
|
);
|
2015-04-02 18:22:00 +03:00
|
|
|
return undefined;
|
|
|
|
}
|
2015-04-14 17:00:00 +03:00
|
|
|
return scopeURL.href;
|
2015-03-26 18:53:51 +03:00
|
|
|
}
|
2014-10-31 21:24:47 +03:00
|
|
|
|
2015-05-27 00:04:59 +03:00
|
|
|
function processStartURLMember() {
|
2015-04-02 18:22:00 +03:00
|
|
|
const spec = {
|
2018-12-18 23:38:43 +03:00
|
|
|
objectName: "manifest",
|
2015-05-27 00:04:59 +03:00
|
|
|
object: rawManifest,
|
2018-12-18 23:38:43 +03:00
|
|
|
property: "start_url",
|
|
|
|
expectedType: "string",
|
|
|
|
trim: false,
|
2015-04-02 18:22:00 +03:00
|
|
|
};
|
2015-05-27 00:04:59 +03:00
|
|
|
let result = new URL(docURL).href;
|
2015-05-20 17:58:00 +03:00
|
|
|
const value = extractor.extractValue(spec);
|
2018-12-18 23:38:43 +03:00
|
|
|
if (value === undefined || value === "") {
|
2015-04-02 18:22:00 +03:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
let potentialResult;
|
|
|
|
try {
|
2015-05-27 00:04:59 +03:00
|
|
|
potentialResult = new URL(value, manifestURL);
|
2015-04-02 18:22:00 +03:00
|
|
|
} catch (e) {
|
2018-12-18 23:38:43 +03:00
|
|
|
console.warn(domBundle.GetStringFromName("ManifestStartURLInvalid"));
|
2015-04-02 18:22:00 +03:00
|
|
|
return result;
|
|
|
|
}
|
2015-05-27 00:04:59 +03:00
|
|
|
if (potentialResult.origin !== docURL.origin) {
|
2019-07-05 11:44:55 +03:00
|
|
|
console.warn(
|
|
|
|
domBundle.GetStringFromName("ManifestStartURLShouldBeSameOrigin")
|
|
|
|
);
|
2015-04-02 18:22:00 +03:00
|
|
|
} else {
|
2015-04-14 17:00:00 +03:00
|
|
|
result = potentialResult.href;
|
2015-03-26 18:53:51 +03:00
|
|
|
}
|
2015-04-02 18:22:00 +03:00
|
|
|
return result;
|
2014-10-31 21:24:47 +03:00
|
|
|
}
|
2015-05-20 17:58:00 +03:00
|
|
|
|
2015-05-27 00:04:59 +03:00
|
|
|
function processThemeColorMember() {
|
2015-05-20 17:58:00 +03:00
|
|
|
const spec = {
|
2018-12-18 23:38:43 +03:00
|
|
|
objectName: "manifest",
|
2015-05-27 00:04:59 +03:00
|
|
|
object: rawManifest,
|
2018-12-18 23:38:43 +03:00
|
|
|
property: "theme_color",
|
|
|
|
expectedType: "string",
|
|
|
|
trim: true,
|
2015-05-20 17:58:00 +03:00
|
|
|
};
|
|
|
|
return extractor.extractColorValue(spec);
|
|
|
|
}
|
2015-05-25 23:35:33 +03:00
|
|
|
|
2016-01-19 21:16:02 +03:00
|
|
|
function processBackgroundColorMember() {
|
|
|
|
const spec = {
|
2018-12-18 23:38:43 +03:00
|
|
|
objectName: "manifest",
|
2016-01-19 21:16:02 +03:00
|
|
|
object: rawManifest,
|
2018-12-18 23:38:43 +03:00
|
|
|
property: "background_color",
|
|
|
|
expectedType: "string",
|
|
|
|
trim: true,
|
2016-01-19 21:16:02 +03:00
|
|
|
};
|
|
|
|
return extractor.extractColorValue(spec);
|
|
|
|
}
|
|
|
|
|
2015-05-27 00:04:59 +03:00
|
|
|
function processLangMember() {
|
2015-05-25 23:35:33 +03:00
|
|
|
const spec = {
|
2018-12-18 23:38:43 +03:00
|
|
|
objectName: "manifest",
|
2015-05-27 00:04:59 +03:00
|
|
|
object: rawManifest,
|
2018-12-18 23:38:43 +03:00
|
|
|
property: "lang",
|
2019-07-05 11:44:55 +03:00
|
|
|
expectedType: "string",
|
|
|
|
trim: true,
|
2015-05-25 23:35:33 +03:00
|
|
|
};
|
2019-04-11 04:35:21 +03:00
|
|
|
return extractor.extractLanguageValue(spec);
|
2015-05-25 23:35:33 +03:00
|
|
|
}
|
2018-12-18 23:38:43 +03:00
|
|
|
},
|
2015-04-02 18:22:00 +03:00
|
|
|
};
|
2018-12-18 23:38:43 +03:00
|
|
|
var EXPORTED_SYMBOLS = ["ManifestProcessor"]; // jshint ignore:line
|