Bug 1164235 - Add theme_color support to web manifest processor. r=ehsan

This commit is contained in:
Marcos Caceres 2015-05-20 10:58:00 -04:00
parent beac1a1951
commit bd3bcefb9f
5 changed files with 122 additions and 89 deletions

View File

@ -14,11 +14,11 @@
*
* Usage:
*
* .process(aManifest, aBaseURL, aMemberName, console);
* .process(aManifest, aBaseURL, aMemberName);
*
*/
/*exported EXPORTED_SYMBOLS */
/*globals extractValue, Components*/
/*exported EXPORTED_SYMBOLS*/
/*globals Components*/
'use strict';
this.EXPORTED_SYMBOLS = ['ManifestImageObjectProcessor']; // jshint ignore:line
const imports = {};
@ -27,19 +27,15 @@ const {
classes: Cc,
interfaces: Ci
} = Components;
const scriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1']
.getService(Ci.mozIJSSubScriptLoader);
scriptLoader.loadSubScript(
'resource://gre/modules/manifestValueExtractor.js',
this); // jshint ignore:line
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.importGlobalProperties(['URL']);
imports.netutil = Cc['@mozilla.org/network/util;1']
.getService(Ci.nsINetUtil);
imports.DOMUtils = Cc['@mozilla.org/inspector/dom-utils;1']
.getService(Ci.inIDOMUtils);
function ManifestImageObjectProcessor() {}
function ManifestImageObjectProcessor(aConsole, aExtractor) {
this.console = aConsole;
this.extractor = aExtractor;
}
// Static getters
Object.defineProperties(ManifestImageObjectProcessor, {
@ -55,8 +51,8 @@ Object.defineProperties(ManifestImageObjectProcessor, {
}
});
ManifestImageObjectProcessor.process = function(
aManifest, aBaseURL, aMemberName, console
ManifestImageObjectProcessor.prototype.process = function(
aManifest, aBaseURL, aMemberName
) {
const spec = {
objectName: 'manifest',
@ -65,8 +61,9 @@ ManifestImageObjectProcessor.process = function(
expectedType: 'array',
trim: false
};
const extractor = this.extractor;
const images = [];
const value = extractValue(spec, console);
const value = extractor.extractValue(spec);
if (Array.isArray(value)) {
// Filter out images whose "src" is not useful.
value.filter(item => !!processSrcMember(item, aBaseURL))
@ -95,7 +92,7 @@ ManifestImageObjectProcessor.process = function(
expectedType: 'string',
trim: true
};
let value = extractValue(spec, console);
let value = extractor.extractValue(spec);
if (value) {
value = imports.netutil.parseContentType(value, charset, hadCharset);
}
@ -117,7 +114,7 @@ ManifestImageObjectProcessor.process = function(
expectedType: 'string',
trim: false
};
const value = extractValue(spec, console);
const value = extractor.extractValue(spec);
let url;
if (value && value.length) {
try {
@ -136,7 +133,7 @@ ManifestImageObjectProcessor.process = function(
expectedType: 'string',
trim: true
};
const value = extractValue(spec, console);
const value = extractor.extractValue(spec);
if (value) {
// Split on whitespace and filter out invalid values.
value.split(/\s+/)
@ -171,15 +168,7 @@ ManifestImageObjectProcessor.process = function(
expectedType: 'string',
trim: true
};
const value = extractValue(spec, console);
let color;
if (imports.DOMUtils.isValidCSSColor(value)) {
color = value;
} else {
const msg = `background_color: ${value} is not a valid CSS color.`;
console.warn(msg);
}
return color;
return extractor.extractColorValue(spec);
}
};
this.ManifestImageObjectProcessor = ManifestImageObjectProcessor; // jshint ignore:line

View File

@ -21,29 +21,17 @@
*/
/*exported EXPORTED_SYMBOLS */
/*JSLint options in comment below: */
/*globals Components, XPCOMUtils, extractValue*/
/*globals Components, XPCOMUtils*/
'use strict';
this.EXPORTED_SYMBOLS = ['ManifestProcessor']; // jshint ignore:line
const imports = {};
const {
utils: Cu,
classes: Cc,
interfaces: Ci
utils: Cu
} = Components;
const scriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1']
.getService(Ci.mozIJSSubScriptLoader);
//Add extractValue() helper to this context.
scriptLoader.loadSubScript(
'resource://gre/modules/manifestValueExtractor.js',
this); // jshint ignore:line
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.importGlobalProperties(['URL']);
XPCOMUtils.defineLazyModuleGetter(imports, 'Services',
'resource://gre/modules/Services.jsm');
XPCOMUtils.defineLazyModuleGetter(imports, 'ManifestImageObjectProcessor',
'resource://gre/modules/ManifestImageObjectProcessor.jsm');
imports.netutil = Cc['@mozilla.org/network/util;1']
.getService(Ci.nsINetUtil);
const displayModes = new Set(['fullscreen', 'standalone', 'minimal-ui',
'browser'
]);
@ -54,6 +42,13 @@ const orientationTypes = new Set(['any', 'natural', 'landscape', 'portrait',
const {
ConsoleAPI
} = Cu.import('resource://gre/modules/devtools/Console.jsm');
const {
ManifestImageObjectProcessor: ImgObjProcessor
} = Cu.import('resource://gre/modules/ManifestImageObjectProcessor.jsm');
const {
ManifestValueExtractor
} = Cu.import('resource://gre/modules/ManifestValueExtractor.jsm');
function ManifestProcessor() {}
@ -77,22 +72,22 @@ Object.defineProperties(ManifestProcessor, {
});
ManifestProcessor.prototype = {
// process method: processes JSON text into a clean manifest
// process() method processes JSON text into a clean manifest
// that conforms with the W3C specification. Takes an object
// expecting the following dictionary items:
// * aJsonText: the JSON string to be processed.
// * aManifestURL: the URL of the manifest, to resolve URLs.
// * aDocURL: the URL of the owner doc, for security checks.
// * aDocURL: the URL of the owner doc, for security checks
process({
jsonText: aJsonText,
manifestURL: aManifestURL,
docURL: aDocURL
}) {
const manifestURL = new URL(aManifestURL);
const docURL = new URL(aDocURL);
const console = new ConsoleAPI({
prefix: 'Web Manifest: '
});
const manifestURL = new URL(aManifestURL);
const docURL = new URL(aDocURL);
let rawManifest = {};
try {
rawManifest = JSON.parse(aJsonText);
@ -102,18 +97,21 @@ ManifestProcessor.prototype = {
console.warn(msg);
rawManifest = {};
}
const extractor = new ManifestValueExtractor(console);
const imgObjProcessor = new ImgObjProcessor(console, extractor);
const processedManifest = {
'start_url': processStartURLMember(rawManifest, manifestURL, docURL),
'display': processDisplayMember(rawManifest),
'orientation': processOrientationMember(rawManifest),
'name': processNameMember(rawManifest),
'icons': imports.ManifestImageObjectProcessor.process(
rawManifest, manifestURL, 'icons', console
'icons': imgObjProcessor.process(
rawManifest, manifestURL, 'icons'
),
'splash_screens': imports.ManifestImageObjectProcessor.process(
rawManifest, manifestURL, 'splash_screens', console
'splash_screens': imgObjProcessor.process(
rawManifest, manifestURL, 'splash_screens'
),
'short_name': processShortNameMember(rawManifest),
'theme_color': processThemeColorMember(rawManifest),
};
processedManifest.scope = processScopeMember(rawManifest, manifestURL,
docURL, new URL(processedManifest['start_url'])); // jshint ignore:line
@ -128,7 +126,7 @@ ManifestProcessor.prototype = {
expectedType: 'string',
trim: true
};
return extractValue(spec, console);
return extractor.extractValue(spec);
}
function processShortNameMember(aManifest) {
@ -139,7 +137,7 @@ ManifestProcessor.prototype = {
expectedType: 'string',
trim: true
};
return extractValue(spec, console);
return extractor.extractValue(spec);
}
function processOrientationMember(aManifest) {
@ -150,7 +148,7 @@ ManifestProcessor.prototype = {
expectedType: 'string',
trim: true
};
const value = extractValue(spec, console);
const value = extractor.extractValue(spec);
if (ManifestProcessor.orientationTypes.has(value)) {
return value;
}
@ -166,7 +164,7 @@ ManifestProcessor.prototype = {
expectedType: 'string',
trim: true
};
const value = extractValue(spec, console);
const value = extractor.extractValue(spec);
if (ManifestProcessor.displayModes.has(value)) {
return value;
}
@ -182,7 +180,7 @@ ManifestProcessor.prototype = {
trim: false
};
let scopeURL;
const value = extractValue(spec, console);
const value = extractor.extractValue(spec);
if (value === undefined || value === '') {
return undefined;
}
@ -218,7 +216,7 @@ ManifestProcessor.prototype = {
trim: false
};
let result = new URL(aDocURL).href;
const value = extractValue(spec, console);
const value = extractor.extractValue(spec);
if (value === undefined || value === '') {
return result;
}
@ -237,6 +235,18 @@ ManifestProcessor.prototype = {
}
return result;
}
function processThemeColorMember(aManifest) {
const spec = {
objectName: 'manifest',
object: aManifest,
property: 'theme_color',
expectedType: 'string',
trim: true
};
return extractor.extractColorValue(spec);
}
}
};
this.ManifestProcessor = ManifestProcessor; // jshint ignore:line

View File

@ -0,0 +1,68 @@
/* 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 https://www.mozilla.org/MPL/2.0/. */
/*
* Helper functions extract values from manifest members
* and reports conformance violations.
*/
/*globals Components*/
'use strict';
const imports = {};
const {
classes: Cc,
interfaces: Ci
} = Components;
imports.DOMUtils = Cc['@mozilla.org/inspector/dom-utils;1']
.getService(Ci.inIDOMUtils);
this.EXPORTED_SYMBOLS = ['ManifestValueExtractor']; // jshint ignore:line
function ManifestValueExtractor(aConsole) {
this.console = aConsole;
}
ManifestValueExtractor.prototype = {
// This function takes a 'spec' object and destructures
// it to extract a value. If the value is of th wrong type, it
// warns the developer and returns undefined.
// expectType: is the type of a JS primitive (string, number, etc.)
// object: is the object from which to extract the value.
// objectName: string used to construct the developer warning.
// property: the name of the property being extracted.
// trim: boolean, if the value should be trimmed (used by string type).
extractValue({
expectedType, object, objectName, property, trim
}) {
const value = object[property];
const isArray = Array.isArray(value);
// We need to special-case "array", as it's not a JS primitive.
const type = (isArray) ? 'array' : typeof value;
if (type !== expectedType) {
if (type !== 'undefined') {
let msg = `Expected the ${objectName}'s ${property} `;
msg += `member to be a ${expectedType}.`;
this.console.log(msg);
}
return undefined;
}
// Trim string and returned undefined if the empty string.
const shouldTrim = expectedType === 'string' && value && trim;
if (shouldTrim) {
return value.trim() || undefined;
}
return value;
},
extractColorValue(spec) {
const value = this.extractValue(spec);
let color;
if (imports.DOMUtils.isValidCSSColor(value)) {
color = value;
} else {
const msg = `background_color: ${value} is not a valid CSS color.`;
this.console.warn(msg);
}
return color;
}
};
this.ManifestValueExtractor = ManifestValueExtractor; // jshint ignore:line

View File

@ -1,34 +0,0 @@
/* 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/. */
/*
* Helper function extracts values from manifest members
* and reports conformance violations.
*/
function extractValue({
objectName,
object,
property,
expectedType,
trim
}, console) {
const value = object[property];
const isArray = Array.isArray(value);
// We need to special-case "array", as it's not a JS primitive.
const type = (isArray) ? 'array' : typeof value;
if (type !== expectedType) {
if (type !== 'undefined') {
let msg = `Expected the ${objectName}'s ${property} `;
msg += `member to be a ${expectedType}.`;
console.log(msg);
}
return undefined;
}
// Trim string and returned undefined if the empty string.
const shouldTrim = expectedType === 'string' && value && trim;
if (shouldTrim) {
return value.trim() || undefined;
}
return value;
}

View File

@ -8,7 +8,7 @@ EXTRA_JS_MODULES += [
'ManifestImageObjectProcessor.jsm',
'ManifestObtainer.jsm',
'ManifestProcessor.jsm',
'manifestValueExtractor.js'
'ManifestValueExtractor.jsm'
]
MOCHITEST_MANIFESTS += ['test/mochitest.ini']