tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ManifestObtainer.sys.mjs (5414B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
      4 */
      5 /*
      6 * ManifestObtainer is an implementation of:
      7 * http://w3c.github.io/manifest/#obtaining
      8 *
      9 * Exposes 2 public method:
     10 *
     11 *  .contentObtainManifest(aContent) - used in content process
     12 *  .browserObtainManifest(aBrowser) - used in browser/parent process
     13 *
     14 * both return a promise. If successful, you get back a manifest object.
     15 *
     16 * Import it with URL:
     17 *   'chrome://global/content/manifestMessages.js'
     18 *
     19 * e10s IPC message from this components are handled by:
     20 *   dom/ipc/manifestMessages.js
     21 *
     22 * Which is injected into every browser instance via browser.js.
     23 */
     24 
     25 import { ManifestProcessor } from "resource://gre/modules/ManifestProcessor.sys.mjs";
     26 
     27 export var ManifestObtainer = {
     28  /**
     29   * Public interface for obtaining a web manifest from a XUL browser, to use
     30   * on the parent process.
     31   *
     32   * @param  {XULBrowser} The browser to check for the manifest.
     33   * @param {object} aOptions
     34   * @param {boolean} aOptions.checkConformance If spec conformance messages should be collected.
     35   *                                            Adds proprietary moz_* members to manifest.
     36   * @return {Promise<object>} The processed manifest.
     37   */
     38  async browserObtainManifest(
     39    aBrowser,
     40    aOptions = { checkConformance: false }
     41  ) {
     42    if (!isXULBrowser(aBrowser)) {
     43      throw new TypeError("Invalid input. Expected XUL browser.");
     44    }
     45 
     46    const actor =
     47      aBrowser.browsingContext.currentWindowGlobal.getActor("ManifestMessages");
     48 
     49    const reply = await actor.sendQuery(
     50      "DOM:ManifestObtainer:Obtain",
     51      aOptions
     52    );
     53    if (!reply.success) {
     54      const error = toError(reply.result);
     55      throw error;
     56    }
     57    return reply.result;
     58  },
     59  /**
     60   * Public interface for obtaining a web manifest from a XUL browser.
     61   *
     62   * @param {Window} aContent A content Window from which to extract the manifest.
     63   * @param {object} aOptions
     64   * @param {boolean} aOptions.checkConformance If spec conformance messages should be collected.
     65   *                                            Adds proprietary moz_* members to manifest.
     66   * @return {Promise<object>} The processed manifest.
     67   */
     68  async contentObtainManifest(
     69    aContent,
     70    aOptions = { checkConformance: false }
     71  ) {
     72    if (!Services.prefs.getBoolPref("dom.manifest.enabled")) {
     73      throw new Error(
     74        "Obtaining manifest is disabled by pref: dom.manifest.enabled"
     75      );
     76    }
     77    if (!aContent || isXULBrowser(aContent)) {
     78      const err = new TypeError("Invalid input. Expected a DOM Window.");
     79      return Promise.reject(err);
     80    }
     81    const response = await fetchManifest(aContent);
     82    const result = await processResponse(response, aContent, aOptions);
     83    const clone = Cu.cloneInto(result, aContent);
     84    return clone;
     85  },
     86 };
     87 
     88 function toError(aErrorClone) {
     89  let error;
     90  switch (aErrorClone.name) {
     91    case "TypeError":
     92      error = new TypeError();
     93      break;
     94    default:
     95      error = new Error();
     96  }
     97  Object.getOwnPropertyNames(aErrorClone).forEach(
     98    name => (error[name] = aErrorClone[name])
     99  );
    100  return error;
    101 }
    102 
    103 function isXULBrowser(aBrowser) {
    104  if (!aBrowser || !aBrowser.namespaceURI || !aBrowser.localName) {
    105    return false;
    106  }
    107  const XUL_NS =
    108    "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
    109  return aBrowser.namespaceURI === XUL_NS && aBrowser.localName === "browser";
    110 }
    111 
    112 /**
    113 * Asynchronously processes the result of response after having fetched
    114 * a manifest.
    115 *
    116 * @param {Response} aResp Response from fetch().
    117 * @param {Window} aContentWindow The content window.
    118 * @return {Promise<object>} The processed manifest.
    119 */
    120 async function processResponse(aResp, aContentWindow, aOptions) {
    121  const badStatus = aResp.status < 200 || aResp.status >= 300;
    122  if (aResp.type === "error" || badStatus) {
    123    const msg = `Fetch error: ${aResp.status} - ${aResp.statusText} at ${aResp.url}`;
    124    throw new Error(msg);
    125  }
    126  const text = await aResp.text();
    127  const args = {
    128    jsonText: text,
    129    manifestURL: aResp.url,
    130    docURL: aContentWindow.location.href,
    131  };
    132  const processingOptions = Object.assign({}, args, aOptions);
    133  const manifest = ManifestProcessor.process(processingOptions);
    134  return manifest;
    135 }
    136 
    137 /**
    138 * Asynchronously fetches a web manifest.
    139 *
    140 * @param {Window} a The content Window from where to extract the manifest.
    141 * @return {Promise<object>}
    142 */
    143 async function fetchManifest(aWindow) {
    144  if (!aWindow || aWindow.top !== aWindow) {
    145    const msg = "Window must be a top-level browsing context.";
    146    throw new Error(msg);
    147  }
    148  const elem = aWindow.document.querySelector("link[rel~='manifest']");
    149  if (!elem || !elem.getAttribute("href")) {
    150    // There is no actual manifest to fetch, we just return null.
    151    return new aWindow.Response("null");
    152  }
    153  // Throws on malformed URLs
    154  const manifestURL = new aWindow.URL(elem.href, elem.baseURI);
    155  const reqInit = {
    156    credentials: "omit",
    157    mode: "cors",
    158  };
    159  if (elem.crossOrigin === "use-credentials") {
    160    reqInit.credentials = "include";
    161  }
    162  const request = new aWindow.Request(manifestURL, reqInit);
    163  request.overrideContentPolicyType(Ci.nsIContentPolicy.TYPE_WEB_MANIFEST);
    164  // Can reject...
    165  return aWindow.fetch(request);
    166 }