aboutCompat.js (7853B)
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 /* globals browser */ 8 9 let availablePatches; 10 11 const portToAddon = (function () { 12 let port; 13 14 function connect() { 15 port = browser.runtime.connect({ name: "AboutCompatTab" }); 16 port.onMessage.addListener(onMessageFromAddon); 17 port.onDisconnect.addListener(() => { 18 port = undefined; 19 }); 20 } 21 22 connect(); 23 24 async function send(message) { 25 if (port) { 26 return port.postMessage(message); 27 } 28 return Promise.reject("background script port disconnected"); 29 } 30 31 return { send }; 32 })(); 33 34 const $ = function (sel) { 35 return document.querySelector(sel); 36 }; 37 38 const DOMContentLoadedPromise = new Promise(resolve => { 39 document.addEventListener( 40 "DOMContentLoaded", 41 () => { 42 resolve(); 43 }, 44 { once: true } 45 ); 46 }); 47 48 const ua = navigator.userAgent; 49 if (ua.includes("Tablet") || ua.includes("Mobile")) { 50 document.documentElement.classList.add("mobile"); 51 } 52 53 Promise.all([ 54 browser.runtime.sendMessage("getAllInterventions"), 55 DOMContentLoadedPromise, 56 ]).then(([info]) => { 57 // alphabetize the interventions and shims 58 if (info.interventions) { 59 info.interventions = info.interventions.sort((a, b) => 60 a.domain.localeCompare(b.domain) 61 ); 62 } 63 if (info.shims) { 64 info.shims = info.shims.sort((a, b) => a.name.localeCompare(b.name)); 65 } 66 67 document.body.addEventListener("click", async evt => { 68 const ele = evt.target; 69 if (ele.nodeName === "BUTTON") { 70 const row = ele.closest("[data-id]"); 71 if (row) { 72 evt.preventDefault(); 73 ele.disabled = true; 74 const id = row.getAttribute("data-id"); 75 try { 76 await browser.runtime.sendMessage({ command: "toggle", id }); 77 } catch (_) { 78 ele.disabled = false; 79 } 80 } 81 } else if (ele.classList.contains("tab")) { 82 document.querySelectorAll(".tab").forEach(tab => { 83 tab.classList.remove("active"); 84 }); 85 ele.classList.add("active"); 86 } 87 }); 88 89 availablePatches = info; 90 redraw(); 91 }); 92 93 async function onMessageFromAddon(msg) { 94 const alsoShowHidden = location.hash === "#all"; 95 96 await DOMContentLoadedPromise; 97 98 if ("interventionsChanged" in msg) { 99 redrawTable($("#interventions"), msg.interventionsChanged, alsoShowHidden); 100 } 101 102 if ("shimsChanged" in msg) { 103 updateShimTables(msg.shimsChanged, alsoShowHidden); 104 } 105 106 const id = msg.toggling || msg.toggled; 107 const button = $(`[data-id="${id}"] button`); 108 if (!button) { 109 return; 110 } 111 const active = msg.active; 112 document.l10n.setAttributes( 113 button, 114 active ? "label-disable" : "label-enable" 115 ); 116 button.disabled = !!msg.toggling; 117 } 118 119 function redraw() { 120 if (!availablePatches) { 121 return; 122 } 123 const { interventions, shims } = availablePatches; 124 const alsoShowHidden = location.hash === "#all"; 125 redrawTable($("#interventions"), interventions, alsoShowHidden); 126 updateShimTables(shims, alsoShowHidden); 127 } 128 129 function clearTableAndAddMessage(table, msgId) { 130 table.querySelectorAll("tr").forEach(tr => { 131 tr.remove(); 132 }); 133 134 const tr = document.createElement("tr"); 135 tr.className = "message"; 136 tr.id = msgId; 137 138 const td = document.createElement("td"); 139 td.setAttribute("colspan", "3"); 140 document.l10n.setAttributes(td, msgId); 141 tr.appendChild(td); 142 143 table.appendChild(tr); 144 } 145 146 function hideMessagesOnTable(table) { 147 table.querySelectorAll("tr.message").forEach(tr => { 148 tr.remove(); 149 }); 150 } 151 152 function updateShimTables(shimsChanged, alsoShowHidden) { 153 const tables = document.querySelectorAll("table.shims"); 154 if (!tables.length) { 155 return; 156 } 157 158 for (const { bug, disabledReason, hidden, id, name, type } of shimsChanged) { 159 // if any shim is disabled by global pref, all of them are. just show the 160 // "disabled in about:config" message on each shim table in that case. 161 if (disabledReason === "globalPref") { 162 for (const table of tables) { 163 clearTableAndAddMessage(table, "text-disabled-in-about-config"); 164 } 165 return; 166 } 167 168 // otherwise, find which table the shim belongs in. if there is none, 169 // ignore the shim (we're not showing it on the UI for whatever reason). 170 const table = document.querySelector(`table.shims#${type}`); 171 if (!table) { 172 continue; 173 } 174 175 // similarly, skip shims hidden from the UI (only for testing, etc). 176 if (!alsoShowHidden && hidden) { 177 continue; 178 } 179 180 // also, hide the shim if it is disabled because it is not meant for this 181 // platform, release (etc) rather than being disabled by pref/about:compat 182 const notApplicable = 183 disabledReason && 184 disabledReason !== "pref" && 185 disabledReason !== "session"; 186 if (!alsoShowHidden && notApplicable) { 187 continue; 188 } 189 190 // create an updated table-row for the shim 191 const tr = document.createElement("tr"); 192 tr.setAttribute("data-id", id); 193 194 let td = document.createElement("td"); 195 td.innerText = name; 196 tr.appendChild(td); 197 198 td = document.createElement("td"); 199 const a = document.createElement("a"); 200 a.href = `https://bugzilla.mozilla.org/show_bug.cgi?id=${bug}`; 201 document.l10n.setAttributes(a, "label-more-information", { bug }); 202 a.target = "_blank"; 203 td.appendChild(a); 204 tr.appendChild(td); 205 206 td = document.createElement("td"); 207 tr.appendChild(td); 208 const button = document.createElement("button"); 209 document.l10n.setAttributes( 210 button, 211 disabledReason ? "label-enable" : "label-disable" 212 ); 213 td.appendChild(button); 214 215 // is it already in the table? 216 const row = table.querySelector(`tr[data-id="${id}"]`); 217 if (row) { 218 row.replaceWith(tr); 219 } else { 220 table.appendChild(tr); 221 } 222 } 223 224 for (const table of tables) { 225 if (!table.querySelector("tr:not(.message)")) { 226 // no shims? then add a message that none are available for this platform/config 227 clearTableAndAddMessage(table, `text-no-${table.id}`); 228 } else { 229 // otherwise hide any such message, since we have shims on the list 230 hideMessagesOnTable(table); 231 } 232 } 233 } 234 235 function redrawTable(table, data, alsoShowHidden) { 236 const df = document.createDocumentFragment(); 237 table.querySelectorAll("tr").forEach(tr => { 238 tr.remove(); 239 }); 240 241 let noEntriesMessage; 242 if (data === false) { 243 noEntriesMessage = "text-disabled-in-about-config"; 244 } else if (data.length === 0) { 245 noEntriesMessage = `text-no-${table.id}`; 246 } 247 248 if (noEntriesMessage) { 249 const tr = document.createElement("tr"); 250 df.appendChild(tr); 251 252 const td = document.createElement("td"); 253 td.setAttribute("colspan", "3"); 254 document.l10n.setAttributes(td, noEntriesMessage); 255 tr.appendChild(td); 256 257 table.appendChild(df); 258 return; 259 } 260 261 for (const row of data) { 262 if (row.hidden && !alsoShowHidden) { 263 continue; 264 } 265 266 const tr = document.createElement("tr"); 267 tr.setAttribute("data-id", row.id); 268 df.appendChild(tr); 269 270 let td = document.createElement("td"); 271 td.innerText = row.domain; 272 tr.appendChild(td); 273 274 td = document.createElement("td"); 275 const a = document.createElement("a"); 276 const bug = row.bug; 277 a.href = `https://bugzilla.mozilla.org/show_bug.cgi?id=${bug}`; 278 document.l10n.setAttributes(a, "label-more-information", { bug }); 279 a.target = "_blank"; 280 td.appendChild(a); 281 tr.appendChild(td); 282 283 td = document.createElement("td"); 284 tr.appendChild(td); 285 const button = document.createElement("button"); 286 document.l10n.setAttributes( 287 button, 288 row.active ? "label-disable" : "label-enable" 289 ); 290 td.appendChild(button); 291 } 292 table.appendChild(df); 293 } 294 295 window.onhashchange = redraw;