function parseManifest(string, manifestUrl, documentUrl) {
  if (manifestUrl === undefined || documentUrl === undefined) {
    throw new Error('Manifest and document URLs required for manifest parsing.');
  }

  let jsonInput;

  try {
    jsonInput = JSON.parse(string);
  } catch (e) {
    return {
      raw: string,
      value: undefined,
      warning: 'ERROR: file isn\'t valid JSON: ' + e,
      url: manifestUrl,
    };
  }

  const manifest = {
    name: parseName(jsonInput),
    short_name: parseShortName(jsonInput),
    start_url: parseStartUrl(jsonInput, manifestUrl, documentUrl),
    display: parseDisplay(jsonInput),
    orientation: parseOrientation(jsonInput),
    icons: parseIcons(jsonInput, manifestUrl),
    related_applications: parseRelatedApplications(jsonInput),
    prefer_related_applications: parsePreferRelatedApplications(jsonInput),
    theme_color: parseThemeColor(jsonInput),
    background_color: parseBackgroundColor(jsonInput),
  };

  /** @type {string|undefined} */
  let manifestUrlWarning;
  try {
    const manifestUrlParsed = new URL(manifestUrl);
    if (!manifestUrlParsed.protocol.startsWith('http')) {
      manifestUrlWarning = `WARNING: manifest URL not available over a valid network protocol`;
    }
  } catch (_) {
    manifestUrlWarning = `ERROR: invalid manifest URL: '${manifestUrl}'`;
  }

  return {
    raw: string,
    value: manifest,
    warning: manifestUrlWarning,
    url: manifestUrl,
  };
}
Parse a manifest from the given inputs.
@param {string} string Manifest JSON string.
@param {string} manifestUrl URL of manifest file.
@param {string} documentUrl URL of document containing manifest link element.