2015-07-30 18:56:12 +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
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* ManifestObtainer is an implementation of:
|
|
|
|
* http://w3c.github.io/manifest/#obtaining
|
|
|
|
*
|
|
|
|
* Exposes 2 public method:
|
|
|
|
*
|
|
|
|
* .contentObtainManifest(aContent) - used in content process
|
|
|
|
* .browserObtainManifest(aBrowser) - used in browser/parent process
|
|
|
|
*
|
|
|
|
* both return a promise. If successful, you get back a manifest object.
|
|
|
|
*
|
|
|
|
* Import it with URL:
|
|
|
|
* 'chrome://global/content/manifestMessages.js'
|
|
|
|
*
|
|
|
|
* e10s IPC message from this components are handled by:
|
|
|
|
* dom/ipc/manifestMessages.js
|
|
|
|
*
|
|
|
|
* Which is injected into every browser instance via browser.js.
|
|
|
|
*
|
|
|
|
* exported ManifestObtainer
|
|
|
|
*/
|
|
|
|
/*globals Components, Task, PromiseMessage, XPCOMUtils, ManifestProcessor, BrowserUtils*/
|
|
|
|
"use strict";
|
|
|
|
const {
|
|
|
|
utils: Cu,
|
|
|
|
classes: Cc,
|
|
|
|
interfaces: Ci
|
|
|
|
} = Components;
|
|
|
|
Cu.import("resource://gre/modules/PromiseMessage.jsm");
|
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
Cu.import("resource://gre/modules/ManifestProcessor.jsm");
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils", // jshint ignore:line
|
|
|
|
"resource://gre/modules/BrowserUtils.jsm");
|
|
|
|
|
|
|
|
this.ManifestObtainer = { // jshint ignore:line
|
|
|
|
/**
|
|
|
|
* Public interface for obtaining a web manifest from a XUL browser, to use
|
|
|
|
* on the parent process.
|
|
|
|
* @param {XULBrowser} The browser to check for the manifest.
|
|
|
|
* @return {Promise<Object>} The processed manifest.
|
|
|
|
*/
|
2017-06-22 13:51:42 +03:00
|
|
|
async browserObtainManifest(aBrowser) {
|
2015-07-30 18:56:12 +03:00
|
|
|
const msgKey = "DOM:ManifestObtainer:Obtain";
|
|
|
|
if (!isXULBrowser(aBrowser)) {
|
|
|
|
throw new TypeError("Invalid input. Expected XUL browser.");
|
|
|
|
}
|
|
|
|
const mm = aBrowser.messageManager;
|
2017-06-22 13:51:42 +03:00
|
|
|
const {data: {success, result}} = await PromiseMessage.send(mm, msgKey);
|
2015-07-30 18:56:12 +03:00
|
|
|
if (!success) {
|
|
|
|
const error = toError(result);
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
return result;
|
2017-06-22 13:51:42 +03:00
|
|
|
},
|
2015-07-30 18:56:12 +03:00
|
|
|
/**
|
|
|
|
* Public interface for obtaining a web manifest from a XUL browser.
|
|
|
|
* @param {Window} The content Window from which to extract the manifest.
|
|
|
|
* @return {Promise<Object>} The processed manifest.
|
|
|
|
*/
|
2017-06-22 13:51:42 +03:00
|
|
|
async contentObtainManifest(aContent) {
|
2015-07-30 18:56:12 +03:00
|
|
|
if (!aContent || isXULBrowser(aContent)) {
|
|
|
|
throw new TypeError("Invalid input. Expected a DOM Window.");
|
|
|
|
}
|
2016-04-08 00:13:09 +03:00
|
|
|
let manifest;
|
|
|
|
try {
|
2017-06-22 13:51:42 +03:00
|
|
|
manifest = await fetchManifest(aContent);
|
2016-04-08 00:13:09 +03:00
|
|
|
} catch (err) {
|
|
|
|
throw err;
|
|
|
|
}
|
2015-07-30 18:56:12 +03:00
|
|
|
return manifest;
|
2017-06-22 13:51:42 +03:00
|
|
|
}
|
|
|
|
};
|
2015-07-30 18:56:12 +03:00
|
|
|
|
|
|
|
function toError(aErrorClone) {
|
|
|
|
let error;
|
|
|
|
switch (aErrorClone.name) {
|
|
|
|
case "TypeError":
|
|
|
|
error = new TypeError();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
error = new Error();
|
|
|
|
}
|
|
|
|
Object.getOwnPropertyNames(aErrorClone)
|
|
|
|
.forEach(name => error[name] = aErrorClone[name]);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isXULBrowser(aBrowser) {
|
|
|
|
if (!aBrowser || !aBrowser.namespaceURI || !aBrowser.localName) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
|
|
|
return (aBrowser.namespaceURI === XUL && aBrowser.localName === "browser");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Asynchronously processes the result of response after having fetched
|
|
|
|
* a manifest.
|
|
|
|
* @param {Response} aResp Response from fetch().
|
|
|
|
* @param {Window} aContentWindow The content window.
|
|
|
|
* @return {Promise<Object>} The processed manifest.
|
|
|
|
*/
|
2017-06-22 13:51:42 +03:00
|
|
|
const processResponse = async function(aResp, aContentWindow) {
|
2015-07-30 18:56:12 +03:00
|
|
|
const badStatus = aResp.status < 200 || aResp.status >= 300;
|
|
|
|
if (aResp.type === "error" || badStatus) {
|
|
|
|
const msg =
|
|
|
|
`Fetch error: ${aResp.status} - ${aResp.statusText} at ${aResp.url}`;
|
|
|
|
throw new Error(msg);
|
|
|
|
}
|
2017-06-22 13:51:42 +03:00
|
|
|
const text = await aResp.text();
|
2015-07-30 18:56:12 +03:00
|
|
|
const args = {
|
|
|
|
jsonText: text,
|
|
|
|
manifestURL: aResp.url,
|
|
|
|
docURL: aContentWindow.location.href
|
|
|
|
};
|
|
|
|
const manifest = ManifestProcessor.process(args);
|
|
|
|
return manifest;
|
2017-06-22 13:51:42 +03:00
|
|
|
};
|
2015-07-30 18:56:12 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Asynchronously fetches a web manifest.
|
|
|
|
* @param {Window} a The content Window from where to extract the manifest.
|
|
|
|
* @return {Promise<Object>}
|
|
|
|
*/
|
2017-06-22 13:51:42 +03:00
|
|
|
const fetchManifest = async function(aWindow) {
|
2015-07-30 18:56:12 +03:00
|
|
|
if (!aWindow || aWindow.top !== aWindow) {
|
|
|
|
let msg = "Window must be a top-level browsing context.";
|
|
|
|
throw new Error(msg);
|
|
|
|
}
|
|
|
|
const elem = aWindow.document.querySelector("link[rel~='manifest']");
|
|
|
|
if (!elem || !elem.getAttribute("href")) {
|
|
|
|
let msg = `No manifest to fetch at ${aWindow.location}`;
|
|
|
|
throw new Error(msg);
|
|
|
|
}
|
|
|
|
// Throws on malformed URLs
|
|
|
|
const manifestURL = new aWindow.URL(elem.href, elem.baseURI);
|
|
|
|
const reqInit = {
|
|
|
|
mode: "cors"
|
|
|
|
};
|
|
|
|
if (elem.crossOrigin === "use-credentials") {
|
|
|
|
reqInit.credentials = "include";
|
|
|
|
}
|
2016-04-08 00:13:09 +03:00
|
|
|
const request = new aWindow.Request(manifestURL, reqInit);
|
|
|
|
request.overrideContentPolicyType(Ci.nsIContentPolicy.TYPE_WEB_MANIFEST);
|
|
|
|
let response;
|
|
|
|
try {
|
2017-06-22 13:51:42 +03:00
|
|
|
response = await aWindow.fetch(request);
|
2016-04-08 00:13:09 +03:00
|
|
|
} catch (err) {
|
|
|
|
throw err;
|
|
|
|
}
|
2017-06-22 13:51:42 +03:00
|
|
|
const manifest = await processResponse(response, aWindow);
|
2015-07-30 18:56:12 +03:00
|
|
|
return manifest;
|
2017-06-22 13:51:42 +03:00
|
|
|
};
|
2015-07-30 18:56:12 +03:00
|
|
|
|
|
|
|
this.EXPORTED_SYMBOLS = ["ManifestObtainer"]; // jshint ignore:line
|