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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/* globals browser */
let availablePatches;
const portToAddon = (function () {
let port;
function connect() {
port = browser.runtime.connect({ name: "AboutCompatTab" });
port.onMessage.addListener(onMessageFromAddon);
port.onDisconnect.addListener(() => {
port = undefined;
});
}
connect();
async function send(message) {
if (port) {
return port.postMessage(message);
}
return Promise.reject("background script port disconnected");
}
return { send };
})();
const $ = function (sel) {
return document.querySelector(sel);
};
const DOMContentLoadedPromise = new Promise(resolve => {
document.addEventListener(
"DOMContentLoaded",
() => {
resolve();
},
{ once: true }
);
});
Promise.all([
browser.runtime.sendMessage("getAllInterventions"),
DOMContentLoadedPromise,
]).then(([info]) => {
document.body.addEventListener("click", async evt => {
const ele = evt.target;
if (ele.nodeName === "BUTTON") {
const row = ele.closest("[data-id]");
if (row) {
evt.preventDefault();
ele.disabled = true;
const id = row.getAttribute("data-id");
try {
await browser.runtime.sendMessage({ command: "toggle", id });
} catch (_) {
ele.disabled = false;
}
}
} else if (ele.classList.contains("tab")) {
document.querySelectorAll(".tab").forEach(tab => {
tab.classList.remove("active");
});
ele.classList.add("active");
}
});
availablePatches = info;
redraw();
});
async function onMessageFromAddon(msg) {
const alsoShowHidden = location.hash === "#all";
await DOMContentLoadedPromise;
if ("interventionsChanged" in msg) {
redrawTable($("#interventions"), msg.interventionsChanged, alsoShowHidden);
}
if ("overridesChanged" in msg) {
redrawTable($("#overrides"), msg.overridesChanged, alsoShowHidden);
}
if ("shimsChanged" in msg) {
updateShimTables(msg.shimsChanged, alsoShowHidden);
}
const id = msg.toggling || msg.toggled;
const button = $(`[data-id="${id}"] button`);
if (!button) {
return;
}
const active = msg.active;
document.l10n.setAttributes(
button,
active ? "label-disable" : "label-enable"
);
button.disabled = !!msg.toggling;
}
function redraw() {
if (!availablePatches) {
return;
}
const { overrides, interventions, shims } = availablePatches;
const alsoShowHidden = location.hash === "#all";
redrawTable($("#overrides"), overrides, alsoShowHidden);
redrawTable($("#interventions"), interventions, alsoShowHidden);
updateShimTables(shims, alsoShowHidden);
}
function clearTableAndAddMessage(table, msgId) {
table.querySelectorAll("tr").forEach(tr => {
tr.remove();
});
const tr = document.createElement("tr");
tr.className = "message";
tr.id = msgId;
const td = document.createElement("td");
td.setAttribute("colspan", "3");
document.l10n.setAttributes(td, msgId);
tr.appendChild(td);
table.appendChild(tr);
}
function hideMessagesOnTable(table) {
table.querySelectorAll("tr.message").forEach(tr => {
tr.remove();
});
}
function updateShimTables(shimsChanged, alsoShowHidden) {
const tables = document.querySelectorAll("table.shims");
if (!tables.length) {
return;
}
for (const { bug, disabledReason, hidden, id, name, type } of shimsChanged) {
// if any shim is disabled by global pref, all of them are. just show the
// "disabled in about:config" message on each shim table in that case.
if (disabledReason === "globalPref") {
for (const table of tables) {
clearTableAndAddMessage(table, "text-disabled-in-about-config");
}
return;
}
// otherwise, find which table the shim belongs in. if there is none,
// ignore the shim (we're not showing it on the UI for whatever reason).
const table = document.querySelector(`table.shims#${type}`);
if (!table) {
continue;
}
// similarly, skip shims hidden from the UI (only for testing, etc).
if (!alsoShowHidden && hidden) {
continue;
}
// also, hide the shim if it is disabled because it is not meant for this
// platform, release (etc) rather than being disabled by pref/about:compat
const notApplicable =
disabledReason &&
disabledReason !== "pref" &&
disabledReason !== "session";
if (!alsoShowHidden && notApplicable) {
continue;
}
// create an updated table-row for the shim
const tr = document.createElement("tr");
tr.setAttribute("data-id", id);
let td = document.createElement("td");
td.innerText = name;
tr.appendChild(td);
td = document.createElement("td");
const a = document.createElement("a");
document.l10n.setAttributes(a, "label-more-information", { bug });
a.target = "_blank";
td.appendChild(a);
tr.appendChild(td);
td = document.createElement("td");
tr.appendChild(td);
const button = document.createElement("button");
document.l10n.setAttributes(
button,
disabledReason ? "label-enable" : "label-disable"
);
td.appendChild(button);
// is it already in the table?
const row = table.querySelector(`tr[data-id="${id}"]`);
if (row) {
row.replaceWith(tr);
} else {
table.appendChild(tr);
}
}
for (const table of tables) {
if (!table.querySelector("tr:not(.message)")) {
// no shims? then add a message that none are available for this platform/config
clearTableAndAddMessage(table, `text-no-${table.id}`);
} else {
// otherwise hide any such message, since we have shims on the list
hideMessagesOnTable(table);
}
}
}
function redrawTable(table, data, alsoShowHidden) {
const df = document.createDocumentFragment();
table.querySelectorAll("tr").forEach(tr => {
tr.remove();
});
let noEntriesMessage;
if (data === false) {
noEntriesMessage = "text-disabled-in-about-config";
} else if (data.length === 0) {
noEntriesMessage = `text-no-${table.id}`;
}
if (noEntriesMessage) {
const tr = document.createElement("tr");
df.appendChild(tr);
const td = document.createElement("td");
td.setAttribute("colspan", "3");
document.l10n.setAttributes(td, noEntriesMessage);
tr.appendChild(td);
table.appendChild(df);
return;
}
for (const row of data) {
if (row.hidden && !alsoShowHidden) {
continue;
}
const tr = document.createElement("tr");
tr.setAttribute("data-id", row.id);
df.appendChild(tr);
let td = document.createElement("td");
td.innerText = row.domain;
tr.appendChild(td);
td = document.createElement("td");
const a = document.createElement("a");
const bug = row.bug;
document.l10n.setAttributes(a, "label-more-information", { bug });
a.target = "_blank";
td.appendChild(a);
tr.appendChild(td);
td = document.createElement("td");
tr.appendChild(td);
const button = document.createElement("button");
document.l10n.setAttributes(
button,
row.active ? "label-disable" : "label-enable"
);
td.appendChild(button);
}
table.appendChild(df);
}
window.onhashchange = redraw;