diff options
Diffstat (limited to '')
-rw-r--r-- | browser/extensions/webcompat/about-compat/aboutCompat.js | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/browser/extensions/webcompat/about-compat/aboutCompat.js b/browser/extensions/webcompat/about-compat/aboutCompat.js new file mode 100644 index 0000000000..e01b853877 --- /dev/null +++ b/browser/extensions/webcompat/about-compat/aboutCompat.js @@ -0,0 +1,283 @@ +/* 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(e => { + 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(); +}); + +function onMessageFromAddon(msg) { + const alsoShowHidden = location.hash === "#all"; + + 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"); + a.href = `https://bugzilla.mozilla.org/show_bug.cgi?id=${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, + 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; + a.href = `https://bugzilla.mozilla.org/show_bug.cgi?id=${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; |