gecko-dev/dom/manifest/ManifestObtainer.jsm

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

169 lines
5.3 KiB
JavaScript
Raw Normal View History

/* 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.
*/
"use strict";
Bug 1514594: Part 3 - Change ChromeUtils.import API. *** Bug 1514594: Part 3a - Change ChromeUtils.import to return an exports object; not pollute global. r=mccr8 This changes the behavior of ChromeUtils.import() to return an exports object, rather than a module global, in all cases except when `null` is passed as a second argument, and changes the default behavior not to pollute the global scope with the module's exports. Thus, the following code written for the old model: ChromeUtils.import("resource://gre/modules/Services.jsm"); is approximately the same as the following, in the new model: var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); Since the two behaviors are mutually incompatible, this patch will land with a scripted rewrite to update all existing callers to use the new model rather than the old. *** Bug 1514594: Part 3b - Mass rewrite all JS code to use the new ChromeUtils.import API. rs=Gijs This was done using the followng script: https://bitbucket.org/kmaglione/m-c-rewrites/src/tip/processors/cu-import-exports.jsm *** Bug 1514594: Part 3c - Update ESLint plugin for ChromeUtils.import API changes. r=Standard8 Differential Revision: https://phabricator.services.mozilla.com/D16747 *** Bug 1514594: Part 3d - Remove/fix hundreds of duplicate imports from sync tests. r=Gijs Differential Revision: https://phabricator.services.mozilla.com/D16748 *** Bug 1514594: Part 3e - Remove no-op ChromeUtils.import() calls. r=Gijs Differential Revision: https://phabricator.services.mozilla.com/D16749 *** Bug 1514594: Part 3f.1 - Cleanup various test corner cases after mass rewrite. r=Gijs *** Bug 1514594: Part 3f.2 - Cleanup various non-test corner cases after mass rewrite. r=Gijs Differential Revision: https://phabricator.services.mozilla.com/D16750 --HG-- extra : rebase_source : 359574ee3064c90f33bf36c2ebe3159a24cc8895 extra : histedit_source : b93c8f42808b1599f9122d7842d2c0b3e656a594%2C64a3a4e3359dc889e2ab2b49461bab9e27fc10a7
2019-01-17 18:18:31 +00:00
const { ManifestProcessor } = ChromeUtils.import(
"resource://gre/modules/ManifestProcessor.jsm"
);
var ManifestObtainer = {
/**
* 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.
* @param {Object} aOptions
* @param {Boolean} aOptions.checkConformance If spec conformance messages should be collected.
* Adds proprietary moz_* members to manifest.
* @return {Promise<Object>} The processed manifest.
*/
async browserObtainManifest(
aBrowser,
aOptions = { checkConformance: false }
) {
if (!isXULBrowser(aBrowser)) {
throw new TypeError("Invalid input. Expected XUL browser.");
}
const actor = aBrowser.browsingContext.currentWindowGlobal.getActor(
"ManifestMessages"
);
const reply = await actor.sendQuery(
"DOM:ManifestObtainer:Obtain",
aOptions
);
if (!reply.success) {
const error = toError(reply.result);
throw error;
}
return reply.result;
},
/**
* Public interface for obtaining a web manifest from a XUL browser.
* @param {Window} aContent A content Window from which to extract the manifest.
* @param {Object} aOptions
* @param {Boolean} aOptions.checkConformance If spec conformance messages should be collected.
* Adds proprietary moz_* members to manifest.
* @return {Promise<Object>} The processed manifest.
*/
async contentObtainManifest(
aContent,
aOptions = { checkConformance: false }
) {
if (!Services.prefs.getBoolPref("dom.manifest.enabled")) {
throw new Error(
"Obtaining manifest is disabled by pref: dom.manifest.enabled"
);
}
if (!aContent || isXULBrowser(aContent)) {
const err = new TypeError("Invalid input. Expected a DOM Window.");
return Promise.reject(err);
}
const response = await fetchManifest(aContent);
const result = await processResponse(response, aContent, aOptions);
const clone = Cu.cloneInto(result, aContent);
return clone;
},
};
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_NS =
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
return aBrowser.namespaceURI === XUL_NS && 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.
*/
async function processResponse(aResp, aContentWindow, aOptions) {
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);
}
const text = await aResp.text();
const args = {
jsonText: text,
manifestURL: aResp.url,
docURL: aContentWindow.location.href,
};
const processingOptions = Object.assign({}, args, aOptions);
const manifest = ManifestProcessor.process(processingOptions);
return manifest;
}
/**
* Asynchronously fetches a web manifest.
* @param {Window} a The content Window from where to extract the manifest.
* @return {Promise<Object>}
*/
async function fetchManifest(aWindow) {
if (!aWindow || aWindow.top !== aWindow) {
const 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")) {
// There is no actual manifest to fetch, we just return null.
return new aWindow.Response("null");
}
// Throws on malformed URLs
const manifestURL = new aWindow.URL(elem.href, elem.baseURI);
const reqInit = {
credentials: "omit",
mode: "cors",
};
if (elem.crossOrigin === "use-credentials") {
reqInit.credentials = "include";
}
const request = new aWindow.Request(manifestURL, reqInit);
request.overrideContentPolicyType(Ci.nsIContentPolicy.TYPE_WEB_MANIFEST);
// Can reject...
return aWindow.fetch(request);
}
var EXPORTED_SYMBOLS = ["ManifestObtainer"];