Source code
Revision control
Copy as Markdown
Other Tools
/* 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
"use strict";
const {
MANIFEST_CATEGORIES,
MANIFEST_ISSUE_LEVELS,
MANIFEST_MEMBER_VALUE_TYPES,
FETCH_MANIFEST_FAILURE,
FETCH_MANIFEST_START,
FETCH_MANIFEST_SUCCESS,
RESET_MANIFEST,
} = require("resource://devtools/client/application/src/constants.js");
function _processRawManifestIcons(rawIcons) {
// NOTE: about `rawIcons` array we are getting from platform:
// - Icons that do not comform to the spec are filtered out
// - We will always get a `src`
// - We will always get `purpose` with a value (default is `["any"]`)
// - `sizes` may be undefined
// - `type` may be undefined
return rawIcons.map(icon => {
return {
key: {
sizes: Array.isArray(icon.sizes) ? icon.sizes.join(" ") : icon.sizes,
contentType: icon.type,
},
value: {
src: icon.src,
purpose: icon.purpose.join(" "),
},
type: MANIFEST_MEMBER_VALUE_TYPES.ICON,
};
});
}
function _processRawManifestMembers(rawManifest) {
function getCategoryForMember(key) {
switch (key) {
case "name":
case "short_name":
return MANIFEST_CATEGORIES.IDENTITY;
default:
return MANIFEST_CATEGORIES.PRESENTATION;
}
}
function getValueTypeForMember(key) {
switch (key) {
case "start_url":
case "scope":
return MANIFEST_MEMBER_VALUE_TYPES.URL;
case "theme_color":
case "background_color":
return MANIFEST_MEMBER_VALUE_TYPES.COLOR;
default:
return MANIFEST_MEMBER_VALUE_TYPES.STRING;
}
}
const res = {
[MANIFEST_CATEGORIES.IDENTITY]: [],
[MANIFEST_CATEGORIES.PRESENTATION]: [],
};
// filter out extra metadata members (those with moz_ prefix) and icons
const rawMembers = Object.entries(rawManifest).filter(
([key]) => !key.startsWith("moz_") && !(key === "icons")
);
for (const [key, value] of rawMembers) {
const category = getCategoryForMember(key);
const type = getValueTypeForMember(key);
res[category].push({ key, value, type });
}
return res;
}
function _processRawManifestIssues(issues) {
return issues.map(x => {
return {
level: x.warn
? MANIFEST_ISSUE_LEVELS.WARNING
: MANIFEST_ISSUE_LEVELS.ERROR,
message: x.warn || x.error,
type: x.type || null,
};
});
}
function _processRawManifest(rawManifest) {
const res = {
url: rawManifest.moz_manifest_url,
};
// group manifest members by category
Object.assign(res, _processRawManifestMembers(rawManifest));
// process icons
res.icons = _processRawManifestIcons(rawManifest.icons || []);
// process error messages
res.validation = _processRawManifestIssues(rawManifest.moz_validation || []);
return res;
}
function ManifestState() {
return {
errorMessage: "",
isLoading: false,
manifest: undefined,
};
}
function manifestReducer(state = ManifestState(), action) {
switch (action.type) {
case FETCH_MANIFEST_START:
return Object.assign({}, state, {
isLoading: true,
mustLoadManifest: false,
});
case FETCH_MANIFEST_FAILURE:
const { error } = action;
// If we add a redux middleware to log errors, we should move the
// console.error below there.
console.error(error);
return Object.assign({}, state, {
errorMessage: error,
isLoading: false,
manifest: null,
});
case FETCH_MANIFEST_SUCCESS:
// NOTE: we don't get an error when the page does not have a manifest,
// but a `null` value there.
const { manifest } = action;
return Object.assign({}, state, {
errorMessage: "",
isLoading: false,
manifest: manifest ? _processRawManifest(manifest) : null,
});
case RESET_MANIFEST:
const defaultState = ManifestState();
return defaultState;
default:
return state;
}
}
module.exports = {
ManifestState,
manifestReducer,
};