Source code
Revision control
Copy as Markdown
Other Tools
/* 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
"use strict";
/* global ExtensionAPI, ExtensionCommon */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
ServiceRequest: "resource://gre/modules/ServiceRequest.sys.mjs",
});
this.remoteSettings = class RemoteSettingsAPI extends ExtensionAPI {
static COLLECTION = "webcompat-interventions";
getAPI(context) {
const EventManager = ExtensionCommon.EventManager;
const { uuid } = context.extension;
const missingFilesCache = {};
async function missingFiles(obj, basePath) {
if (!obj) {
return false;
}
for (let path of Array.isArray(obj) ? obj : [obj]) {
path = path.includes("/") ? path : `${basePath}/${path}`;
if (path in missingFilesCache) {
if (missingFilesCache[path]) {
return true;
}
continue;
}
missingFilesCache[path] = await new Promise(done => {
const req = new lazy.ServiceRequest();
req.responseType = "text";
req.onload = () => done(false);
req.onerror = () => done(true);
req.open("GET", url);
req.send();
});
if (missingFilesCache[path]) {
return true;
}
}
return false;
}
async function missingFilesInIntervention(desc) {
for (let intervention of desc.interventions) {
const { content_scripts } = intervention;
if (
(await missingFiles(content_scripts?.js, "injections/js")) ||
(await missingFiles(content_scripts?.css, "injections/css"))
) {
return true;
}
}
return false;
}
async function missingFilesInShim(desc) {
if (desc.file) {
if (await missingFiles([desc.file], "shims")) {
return true;
}
}
if (desc.logos) {
if (await missingFiles(desc.logos, "shims")) {
return true;
}
}
const { contentScripts } = desc;
if (contentScripts) {
for (let { css, js } of contentScripts) {
if (await missingFiles(css, "shims")) {
return true;
}
if (await missingFiles(js, "shims")) {
return true;
}
}
}
return false;
}
async function markMissingFiles(update) {
if (update?.interventions) {
for (let desc of Object.values(update.interventions)) {
desc.isMissingFiles = await missingFilesInIntervention(desc);
}
}
if (update?.shims) {
for (let desc of Object.values(update.shims)) {
desc.isMissingFiles = await missingFilesInShim(desc);
}
}
return update;
}
return {
remoteSettings: {
onRemoteSettingsUpdate: new EventManager({
context,
name: "remoteSettings.onRemoteSettingsUpdate",
register: fire => {
const callback = ({ data: { current } }) => {
return markMissingFiles(current[0]).then(update => {
fire.async(update).catch(() => {}); // ignore Message Manager disconnects
});
};
lazy
.RemoteSettings(RemoteSettingsAPI.COLLECTION)
.on("sync", callback);
return () => {
lazy
.RemoteSettings(RemoteSettingsAPI.COLLECTION)
.off("sync", callback);
};
},
}).api(),
async get() {
const current = await lazy
.RemoteSettings(RemoteSettingsAPI.COLLECTION)
.get();
return await markMissingFiles(current[0]);
},
},
};
}
};