mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-06 09:05:45 +00:00
d3edf0e131
Bound EXPORTED_SYMBOLS to `this` in WebManifest.jsm Reduced number of iterations on random tests --- dom/ipc/manifestMessages.js | 17 +--- ...ObjectProcessor.jsm => ImageObjectProcessor.js} | 35 ++++--- .../{ManifestObtainer.jsm => ManifestObtainer.js} | 4 +- ...{ManifestProcessor.jsm => ManifestProcessor.js} | 109 ++++++++++----------- ...anifestValueExtractor.jsm => ValueExtractor.js} | 25 +++-- dom/manifest/WebManifest.jsm | 19 ++++ dom/manifest/moz.build | 9 +- .../test/browser_ManifestObtainer_obtain.js | 9 +- dom/manifest/test/common.js | 32 +++--- 9 files changed, 135 insertions(+), 124 deletions(-) rename dom/manifest/{ManifestImageObjectProcessor.jsm => ImageObjectProcessor.js} (81%) rename dom/manifest/{ManifestObtainer.jsm => ManifestObtainer.js} (95%) rename dom/manifest/{ManifestProcessor.jsm => ManifestProcessor.js} (69%) rename dom/manifest/{ManifestValueExtractor.jsm => ValueExtractor.js} (77%) create mode 100644 dom/manifest/WebManifest.jsm --HG-- rename : dom/manifest/ManifestImageObjectProcessor.jsm => dom/manifest/ImageObjectProcessor.js rename : dom/manifest/ManifestObtainer.jsm => dom/manifest/ManifestObtainer.js rename : dom/manifest/ManifestProcessor.jsm => dom/manifest/ManifestProcessor.js rename : dom/manifest/ManifestValueExtractor.jsm => dom/manifest/ValueExtractor.js
270 lines
8.0 KiB
JavaScript
270 lines
8.0 KiB
JavaScript
/* 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
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
/*
|
|
* 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.
|
|
*
|
|
* .process({jsonText,manifestURL,docURL});
|
|
*
|
|
* Depends on ImageObjectProcessor to process things like
|
|
* icons and splash_screens.
|
|
*
|
|
* TODO: The constructor should accept the UA's supported orientations.
|
|
* TODO: The constructor should accept the UA's supported display modes.
|
|
* TODO: hook up developer tools to console. (1086997).
|
|
*/
|
|
/*globals Components*/
|
|
'use strict';
|
|
const {
|
|
utils: Cu,
|
|
interfaces: Ci,
|
|
classes: Cc
|
|
} = Components;
|
|
Cu.importGlobalProperties(['URL']);
|
|
const displayModes = new Set(['fullscreen', 'standalone', 'minimal-ui',
|
|
'browser'
|
|
]);
|
|
const orientationTypes = new Set(['any', 'natural', 'landscape', 'portrait',
|
|
'portrait-primary', 'portrait-secondary', 'landscape-primary',
|
|
'landscape-secondary'
|
|
]);
|
|
const {
|
|
ConsoleAPI
|
|
} = Cu.import('resource://gre/modules/devtools/Console.jsm', {});
|
|
// ValueExtractor is used by the various processors to get values
|
|
// from the manifest and to report errors.
|
|
const {
|
|
ValueExtractor
|
|
} = Cu.import('resource://gre/modules/ValueExtractor.js', {});
|
|
// ImageObjectProcessor is used to process things like icons and images
|
|
const {
|
|
ImageObjectProcessor
|
|
} = Cu.import('resource://gre/modules/ImageObjectProcessor.js', {});
|
|
|
|
function ManifestProcessor() {}
|
|
|
|
// Static getters
|
|
Object.defineProperties(ManifestProcessor, {
|
|
'defaultDisplayMode': {
|
|
get: function() {
|
|
return 'browser';
|
|
}
|
|
},
|
|
'displayModes': {
|
|
get: function() {
|
|
return displayModes;
|
|
}
|
|
},
|
|
'orientationTypes': {
|
|
get: function() {
|
|
return orientationTypes;
|
|
}
|
|
}
|
|
});
|
|
|
|
ManifestProcessor.prototype = {
|
|
// process() method processes JSON text into a clean manifest
|
|
// that conforms with the W3C specification. Takes an object
|
|
// expecting the following dictionary items:
|
|
// * 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
|
|
process({
|
|
jsonText,
|
|
manifestURL: aManifestURL,
|
|
docURL: aDocURL
|
|
}) {
|
|
const console = new ConsoleAPI({
|
|
prefix: 'Web Manifest: '
|
|
});
|
|
const manifestURL = new URL(aManifestURL);
|
|
const docURL = new URL(aDocURL);
|
|
let rawManifest = {};
|
|
try {
|
|
rawManifest = JSON.parse(jsonText);
|
|
} catch (e) {}
|
|
if (typeof rawManifest !== 'object' || rawManifest === null) {
|
|
let msg = 'Manifest needs to be an object.';
|
|
console.warn(msg);
|
|
rawManifest = {};
|
|
}
|
|
const extractor = new ValueExtractor(console);
|
|
const imgObjProcessor = new ImageObjectProcessor(console, extractor);
|
|
const processedManifest = {
|
|
'lang': processLangMember(),
|
|
'start_url': processStartURLMember(),
|
|
'display': processDisplayMember(),
|
|
'orientation': processOrientationMember(),
|
|
'name': processNameMember(),
|
|
'icons': imgObjProcessor.process(
|
|
rawManifest, manifestURL, 'icons'
|
|
),
|
|
'splash_screens': imgObjProcessor.process(
|
|
rawManifest, manifestURL, 'splash_screens'
|
|
),
|
|
'short_name': processShortNameMember(),
|
|
'theme_color': processThemeColorMember(),
|
|
};
|
|
processedManifest.scope = processScopeMember();
|
|
return processedManifest;
|
|
|
|
function processNameMember() {
|
|
const spec = {
|
|
objectName: 'manifest',
|
|
object: rawManifest,
|
|
property: 'name',
|
|
expectedType: 'string',
|
|
trim: true
|
|
};
|
|
return extractor.extractValue(spec);
|
|
}
|
|
|
|
function processShortNameMember() {
|
|
const spec = {
|
|
objectName: 'manifest',
|
|
object: rawManifest,
|
|
property: 'short_name',
|
|
expectedType: 'string',
|
|
trim: true
|
|
};
|
|
return extractor.extractValue(spec);
|
|
}
|
|
|
|
function processOrientationMember() {
|
|
const spec = {
|
|
objectName: 'manifest',
|
|
object: rawManifest,
|
|
property: 'orientation',
|
|
expectedType: 'string',
|
|
trim: true
|
|
};
|
|
const value = extractor.extractValue(spec);
|
|
if (ManifestProcessor.orientationTypes.has(value)) {
|
|
return value;
|
|
}
|
|
// The spec special-cases orientation to return the empty string.
|
|
return '';
|
|
}
|
|
|
|
function processDisplayMember() {
|
|
const spec = {
|
|
objectName: 'manifest',
|
|
object: rawManifest,
|
|
property: 'display',
|
|
expectedType: 'string',
|
|
trim: true
|
|
};
|
|
const value = extractor.extractValue(spec);
|
|
if (ManifestProcessor.displayModes.has(value)) {
|
|
return value;
|
|
}
|
|
return ManifestProcessor.defaultDisplayMode;
|
|
}
|
|
|
|
function processScopeMember() {
|
|
const spec = {
|
|
objectName: 'manifest',
|
|
object: rawManifest,
|
|
property: 'scope',
|
|
expectedType: 'string',
|
|
trim: false
|
|
};
|
|
let scopeURL;
|
|
const startURL = new URL(processedManifest.start_url);
|
|
const value = extractor.extractValue(spec);
|
|
if (value === undefined || value === '') {
|
|
return undefined;
|
|
}
|
|
try {
|
|
scopeURL = new URL(value, manifestURL);
|
|
} catch (e) {
|
|
let msg = 'The URL of scope is invalid.';
|
|
console.warn(msg);
|
|
return undefined;
|
|
}
|
|
if (scopeURL.origin !== docURL.origin) {
|
|
let msg = 'Scope needs to be same-origin as Document.';
|
|
console.warn(msg);
|
|
return undefined;
|
|
}
|
|
// If start URL is not within scope of scope URL:
|
|
let isSameOrigin = startURL && startURL.origin !== scopeURL.origin;
|
|
if (isSameOrigin || !startURL.pathname.startsWith(scopeURL.pathname)) {
|
|
let msg =
|
|
'The start URL is outside the scope, so scope is invalid.';
|
|
console.warn(msg);
|
|
return undefined;
|
|
}
|
|
return scopeURL.href;
|
|
}
|
|
|
|
function processStartURLMember() {
|
|
const spec = {
|
|
objectName: 'manifest',
|
|
object: rawManifest,
|
|
property: 'start_url',
|
|
expectedType: 'string',
|
|
trim: false
|
|
};
|
|
let result = new URL(docURL).href;
|
|
const value = extractor.extractValue(spec);
|
|
if (value === undefined || value === '') {
|
|
return result;
|
|
}
|
|
let potentialResult;
|
|
try {
|
|
potentialResult = new URL(value, manifestURL);
|
|
} catch (e) {
|
|
console.warn('Invalid URL.');
|
|
return result;
|
|
}
|
|
if (potentialResult.origin !== docURL.origin) {
|
|
let msg = 'start_url must be same origin as document.';
|
|
console.warn(msg);
|
|
} else {
|
|
result = potentialResult.href;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function processThemeColorMember() {
|
|
const spec = {
|
|
objectName: 'manifest',
|
|
object: rawManifest,
|
|
property: 'theme_color',
|
|
expectedType: 'string',
|
|
trim: true
|
|
};
|
|
return extractor.extractColorValue(spec);
|
|
}
|
|
|
|
function processLangMember() {
|
|
const spec = {
|
|
objectName: 'manifest',
|
|
object: rawManifest,
|
|
property: 'lang',
|
|
expectedType: 'string',
|
|
trim: true
|
|
};
|
|
let tag = extractor.extractValue(spec);
|
|
// TODO: Check if tag is structurally valid.
|
|
// Cannot do this because we don't support Intl API on Android.
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=864843
|
|
// https://github.com/tc39/ecma402/issues/5
|
|
// TODO: perform canonicalization on the tag.
|
|
// Can't do this today because there is no direct means to
|
|
// access canonicalization algorithms through Intl API.
|
|
// https://github.com/tc39/ecma402/issues/5
|
|
return tag;
|
|
}
|
|
}
|
|
};
|
|
this.ManifestProcessor = ManifestProcessor; // jshint ignore:line
|
|
this.EXPORTED_SYMBOLS = ['ManifestProcessor']; // jshint ignore:line
|