tor-browser

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

remoteSettings.js (3987B)


      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 "use strict";
      6 
      7 /* global ExtensionAPI, ExtensionCommon */
      8 
      9 const lazy = {};
     10 
     11 ChromeUtils.defineESModuleGetters(lazy, {
     12  RemoteSettings: "resource://services-settings/remote-settings.sys.mjs",
     13  ServiceRequest: "resource://gre/modules/ServiceRequest.sys.mjs",
     14 });
     15 
     16 this.remoteSettings = class RemoteSettingsAPI extends ExtensionAPI {
     17  static COLLECTION = "webcompat-interventions";
     18 
     19  getAPI(context) {
     20    const EventManager = ExtensionCommon.EventManager;
     21    const { uuid } = context.extension;
     22    const missingFilesCache = {};
     23 
     24    async function missingFiles(obj, basePath) {
     25      if (!obj) {
     26        return false;
     27      }
     28      for (let path of Array.isArray(obj) ? obj : [obj]) {
     29        path = path.includes("/") ? path : `${basePath}/${path}`;
     30        if (path in missingFilesCache) {
     31          if (missingFilesCache[path]) {
     32            return true;
     33          }
     34          continue;
     35        }
     36        const url = `moz-extension://${uuid}/${path}`;
     37        missingFilesCache[path] = await new Promise(done => {
     38          const req = new lazy.ServiceRequest();
     39          req.responseType = "text";
     40          req.onload = () => done(false);
     41          req.onerror = () => done(true);
     42          req.open("GET", url);
     43          req.send();
     44        });
     45        if (missingFilesCache[path]) {
     46          return true;
     47        }
     48      }
     49      return false;
     50    }
     51 
     52    async function missingFilesInIntervention(desc) {
     53      for (let intervention of desc.interventions) {
     54        const { content_scripts } = intervention;
     55        if (
     56          (await missingFiles(content_scripts?.js, "injections/js")) ||
     57          (await missingFiles(content_scripts?.css, "injections/css"))
     58        ) {
     59          return true;
     60        }
     61      }
     62      return false;
     63    }
     64 
     65    async function missingFilesInShim(desc) {
     66      if (desc.file) {
     67        if (await missingFiles([desc.file], "shims")) {
     68          return true;
     69        }
     70      }
     71      if (desc.logos) {
     72        if (await missingFiles(desc.logos, "shims")) {
     73          return true;
     74        }
     75      }
     76      const { contentScripts } = desc;
     77      if (contentScripts) {
     78        for (let { css, js } of contentScripts) {
     79          if (await missingFiles(css, "shims")) {
     80            return true;
     81          }
     82          if (await missingFiles(js, "shims")) {
     83            return true;
     84          }
     85        }
     86      }
     87      return false;
     88    }
     89 
     90    async function markMissingFiles(update) {
     91      if (update?.interventions) {
     92        for (let desc of Object.values(update.interventions)) {
     93          desc.isMissingFiles = await missingFilesInIntervention(desc);
     94        }
     95      }
     96      if (update?.shims) {
     97        for (let desc of Object.values(update.shims)) {
     98          desc.isMissingFiles = await missingFilesInShim(desc);
     99        }
    100      }
    101      return update;
    102    }
    103 
    104    return {
    105      remoteSettings: {
    106        onRemoteSettingsUpdate: new EventManager({
    107          context,
    108          name: "remoteSettings.onRemoteSettingsUpdate",
    109          register: fire => {
    110            const callback = ({ data: { current } }) => {
    111              return markMissingFiles(current[0]).then(update => {
    112                fire.async(update).catch(() => {}); // ignore Message Manager disconnects
    113              });
    114            };
    115            lazy
    116              .RemoteSettings(RemoteSettingsAPI.COLLECTION)
    117              .on("sync", callback);
    118            return () => {
    119              lazy
    120                .RemoteSettings(RemoteSettingsAPI.COLLECTION)
    121                .off("sync", callback);
    122            };
    123          },
    124        }).api(),
    125        async get() {
    126          const current = await lazy
    127            .RemoteSettings(RemoteSettingsAPI.COLLECTION)
    128            .get();
    129          return await markMissingFiles(current[0]);
    130        },
    131      },
    132    };
    133  }
    134 };