| LEFT | RIGHT | 
|---|
| 1 /* | 1 /* | 
| 2  * This file is part of Adblock Plus <https://adblockplus.org/>, | 2  * This file is part of Adblock Plus <https://adblockplus.org/>, | 
| 3  * Copyright (C) 2006-present eyeo GmbH | 3  * Copyright (C) 2006-present eyeo GmbH | 
| 4  * | 4  * | 
| 5  * Adblock Plus is free software: you can redistribute it and/or modify | 5  * Adblock Plus is free software: you can redistribute it and/or modify | 
| 6  * it under the terms of the GNU General Public License version 3 as | 6  * it under the terms of the GNU General Public License version 3 as | 
| 7  * published by the Free Software Foundation. | 7  * published by the Free Software Foundation. | 
| 8  * | 8  * | 
| 9  * Adblock Plus is distributed in the hope that it will be useful, | 9  * Adblock Plus is distributed in the hope that it will be useful, | 
| 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 161   { | 161   { | 
| 162     for (let element of elements) | 162     for (let element of elements) | 
| 163     { | 163     { | 
| 164       if (element.innerText.search(query) != -1) | 164       if (element.innerText.search(query) != -1) | 
| 165         return false; | 165         return false; | 
| 166     } | 166     } | 
| 167   } | 167   } | 
| 168   return true; | 168   return true; | 
| 169 } | 169 } | 
| 170 | 170 | 
| 171 function performSearch(table, query) | 171 function performSearch(rows, query) | 
| 172 { | 172 { | 
| 173   for (let row of table.rows) | 173   for (let row of rows) | 
| 174   { | 174   { | 
| 175     if (shouldFilterRow(row, query)) | 175     if (shouldFilterRow(row, query)) | 
| 176       row.classList.add("filtered-by-search"); | 176       row.classList.add("filtered-by-search"); | 
| 177     else | 177     else | 
| 178       row.classList.remove("filtered-by-search"); | 178       row.classList.remove("filtered-by-search"); | 
| 179   } | 179   } | 
| 180 } | 180 } | 
| 181 | 181 | 
| 182 function cancelSearch(table) | 182 function cancelSearch(table) | 
| 183 { | 183 { | 
| 184   for (let row of table.rows) | 184   for (let row of table.rows) | 
| 185     row.classList.remove("filtered-by-search"); | 185     row.classList.remove("filtered-by-search"); | 
| 186 } | 186 } | 
| 187 | 187 | 
| 188 /** | 188 function createZebra(rows, state, type) | 
| 189  * Return a generic <tr> placeholder | 189 { | 
| 190  * double linked with the <tr> itself to simplify | 190   let i = 0; | 
| 191  * tbody swaps / filtering of undesired rows. | 191   for (const tr of rows) | 
| 192  * @param {HTMLTableRowElement} tr | 192   { | 
| 193  * @returns {Comment} | 193     tr.hidden = (state ? tr.dataset.state !== state : false) || | 
| 194  */ | 194                 (type ? tr.dataset.type !== type : false); | 
| 195 function getPlaceholder(tr) | 195 | 
| 196 { | 196     if (!tr.hidden) | 
| 197   let placeholder = tr.placeholder; | 197       tr.classList.toggle("odd", !!(i++ % 2)); | 
| 198   if (!placeholder) | 198   } | 
| 199   { |  | 
| 200     placeholder = document.createComment("placeholder"); |  | 
| 201     tr.placeholder = placeholder; |  | 
| 202     placeholder.placeholder = tr; |  | 
| 203   } |  | 
| 204   return placeholder; |  | 
| 205 } |  | 
| 206 |  | 
| 207 /** |  | 
| 208  * Put back all hidden table rows and eventually |  | 
| 209  * filters those that do not match the state or type. |  | 
| 210  * @param {HTMLTableSectionElement} tbody |  | 
| 211  * @param {DOMStringMap} dataset |  | 
| 212  */ |  | 
| 213 function updateRows(tbody, dataset) |  | 
| 214 { |  | 
| 215   // use filters to match nodes or retrieve them later on |  | 
| 216   const selector = getCSSFilter(dataset); |  | 
| 217   for (const node of tbody.childNodes) |  | 
| 218   { |  | 
| 219     // comments linked to table rows that |  | 
| 220     // won't match selector, will be used |  | 
| 221     // to place their linked row back in |  | 
| 222     if ( |  | 
| 223       node.nodeType === Node.COMMENT_NODE && |  | 
| 224       (!selector || !node.placeholder.matches(selector)) |  | 
| 225     ) |  | 
| 226     { |  | 
| 227       tbody.replaceChild(node.placeholder, node); |  | 
| 228     } |  | 
| 229   } |  | 
| 230 |  | 
| 231   // if there is a list of nodes to filter |  | 
| 232   if (selector) |  | 
| 233   { |  | 
| 234     // every matched row should bre replaced with its placeholder |  | 
| 235     for (const tr of tbody.querySelectorAll(selector)) |  | 
| 236     { |  | 
| 237       tbody.replaceChild(getPlaceholder(tr), tr); |  | 
| 238     } |  | 
| 239   } |  | 
| 240 } |  | 
| 241 |  | 
| 242 function getCSSFilter(dataset) |  | 
| 243 { |  | 
| 244   const {filterState, filterType} = dataset; |  | 
| 245 |  | 
| 246   // create a list of possible CSS filter |  | 
| 247   // excluding #background-items from the list of rows to consider |  | 
| 248   const query = []; |  | 
| 249   if (filterState) |  | 
| 250   { |  | 
| 251     query.push(`tr:not(#background-items):not([data-state="${filterState}"])`); |  | 
| 252   } |  | 
| 253   if (filterType) |  | 
| 254   { |  | 
| 255     query.push(`tr:not(#background-items):not([data-type="${filterType}"])`); |  | 
| 256   } |  | 
| 257   return query.join(","); |  | 
| 258 } | 199 } | 
| 259 | 200 | 
| 260 document.addEventListener("DOMContentLoaded", () => | 201 document.addEventListener("DOMContentLoaded", () => | 
| 261 { | 202 { | 
| 262   let container = document.getElementById("items"); | 203   const container = document.getElementById("items"); | 
| 263   let table = container.querySelector("tbody"); | 204   const foregroundTable = container.querySelector("table.foreground tbody"); | 
| 264   let bgItems = container.querySelector("#background-items"); | 205   const backgroundTable = container.querySelector("table.background tbody"); | 
| 265   let template = document.querySelector("template").content.firstElementChild; | 206   const template = document.querySelector("template") | 
|  | 207                             .content.firstElementChild; | 
|  | 208 | 
|  | 209   // We update rows by message index. | 
|  | 210   // Since we don't have a linear collection of rows anymore, | 
|  | 211   // all records, as row, will be indexed in here. | 
|  | 212   // Memory wise, this is just a collection that points | 
|  | 213   // at live DOM nodes (rows), so this does not imply | 
|  | 214   // higher memory consumption, leaks, or any other concern. | 
| 266   const records = []; | 215   const records = []; | 
|  | 216 | 
|  | 217   const filterState = document.getElementById("filter-state"); | 
|  | 218   const filterType = document.getElementById("filter-type"); | 
|  | 219 | 
|  | 220   const updateRowsState = () => | 
|  | 221   { | 
|  | 222     const state = filterState.value; | 
|  | 223     const type = filterType.value; | 
|  | 224     createZebra(foregroundTable.querySelectorAll("tr"), state, type); | 
|  | 225     const notFirstTR = "tr:not(:first-child)"; | 
|  | 226     createZebra(backgroundTable.querySelectorAll(notFirstTR), state, type); | 
|  | 227   }; | 
| 267 | 228 | 
| 268   document.getElementById("reload").addEventListener("click", () => | 229   document.getElementById("reload").addEventListener("click", () => | 
| 269   { | 230   { | 
| 270     ext.devtools.inspectedWindow.reload(); | 231     ext.devtools.inspectedWindow.reload(); | 
| 271   }, false); | 232   }, false); | 
| 272 | 233 | 
| 273   document.getElementById("filter-state").addEventListener("change", (event) => | 234   // update rows visibility state per each change | 
| 274   { | 235   filterState.addEventListener("change", updateRowsState, false); | 
| 275     container.dataset.filterState = event.target.value; | 236   filterType.addEventListener("change", updateRowsState, false); | 
| 276     updateRows(table, container.dataset); |  | 
| 277   }, false); |  | 
| 278 |  | 
| 279   document.getElementById("filter-type").addEventListener("change", (event) => |  | 
| 280   { |  | 
| 281     container.dataset.filterType = event.target.value; |  | 
| 282     updateRows(table, container.dataset); |  | 
| 283   }, false); |  | 
| 284 | 237 | 
| 285   ext.onMessage.addListener((message) => | 238   ext.onMessage.addListener((message) => | 
| 286   { | 239   { | 
| 287     switch (message.type) | 240     switch (message.type) | 
| 288     { | 241     { | 
| 289       case "add-record": | 242       case "add-record": | 
| 290         let record = createRecord(message.request, message.filter, template); | 243         const record = createRecord(message.request, message.filter, template); | 
| 291         records.push(record); | 244         records.push(record); | 
| 292         // avoid pushing rows that are being filtered | 245         // waiting for 6402 to ship | 
| 293         // and use their placeholder when that is the case |  | 
| 294         let selector = getCSSFilter(container.dataset); |  | 
| 295         let node = (!selector || !record.matches(selector)) ? |  | 
| 296                     record : getPlaceholder(record); |  | 
| 297         if (message.request.docDomain == null) | 246         if (message.request.docDomain == null) | 
| 298         { | 247           backgroundTable.appendChild(record); | 
| 299           table.appendChild(node); |  | 
| 300         } |  | 
| 301         else | 248         else | 
| 302         { | 249           foregroundTable.appendChild(record); | 
| 303           table.insertBefore(node, bgItems); |  | 
| 304         } |  | 
| 305         break; | 250         break; | 
| 306 | 251 | 
| 307       case "update-record": | 252       case "update-record": | 
| 308         let oldRow = records[message.index]; | 253         let oldRow = records[message.index]; | 
| 309         let newRow = createRecord(message.request, message.filter, template); | 254         let newRow = createRecord(message.request, message.filter, template); | 
| 310         records[message.index] = newRow; | 255         records[message.index] = newRow; | 
| 311         let liveRow = oldRow.parentNode ? oldRow : oldRow.placeholder; | 256         oldRow.parentNode.replaceChild(newRow, oldRow); | 
| 312         liveRow.parentNode.replaceChild(newRow, liveRow); |  | 
| 313         newRow.classList.add("changed"); | 257         newRow.classList.add("changed"); | 
| 314         container.classList.add("has-changes"); | 258         container.classList.add("has-changes"); | 
| 315         break; | 259         break; | 
| 316 | 260 | 
| 317       case "remove-record": | 261       case "remove-record": | 
| 318         let row = records.splice(message.index, 1)[0]; | 262         let row = records.splice(message.index, 1)[0]; | 
| 319         let live = row.parentNode ? row : row.placeholder; | 263         row.parentNode.removeChild(row); | 
| 320         live.parentNode.removeChild(live); |  | 
| 321         container.classList.add("has-changes"); | 264         container.classList.add("has-changes"); | 
| 322         break; | 265         break; | 
| 323 | 266 | 
| 324       case "reset": | 267       case "reset": | 
| 325         records.splice(0); | 268         records.splice(0); | 
| 326         table.innerHTML = ""; | 269         foregroundTable.innerHTML = ""; | 
| 327         container.classList.remove("has-changes"); | 270         container.classList.remove("has-changes"); | 
| 328         break; | 271         break; | 
| 329     } | 272     } | 
|  | 273     updateRowsState(); | 
| 330   }); | 274   }); | 
| 331 | 275 | 
| 332   window.addEventListener("message", (event) => | 276   window.addEventListener("message", (event) => | 
| 333   { | 277   { | 
| 334     switch (event.data.type) | 278     switch (event.data.type) | 
| 335     { | 279     { | 
| 336       case "performSearch": | 280       case "performSearch": | 
| 337         performSearch(table, event.data.queryString); | 281         performSearch(records, event.data.queryString); | 
| 338         lastFilterQuery = event.data.queryString; | 282         lastFilterQuery = event.data.queryString; | 
| 339         break; | 283         break; | 
| 340       case "cancelSearch": | 284       case "cancelSearch": | 
| 341         cancelSearch(table); | 285         cancelSearch(records); | 
| 342         lastFilterQuery = null; | 286         lastFilterQuery = null; | 
| 343         break; | 287         break; | 
| 344     } | 288     } | 
| 345   }); | 289   }); | 
| 346 | 290 | 
| 347   // Since Chrome 54 the themeName is accessible, for earlier versions we must | 291   // Since Chrome 54 the themeName is accessible, for earlier versions we must | 
| 348   // assume the default theme is being used. | 292   // assume the default theme is being used. | 
| 349   // https://bugs.chromium.org/p/chromium/issues/detail?id=608869 | 293   // https://bugs.chromium.org/p/chromium/issues/detail?id=608869 | 
| 350   let theme = browser.devtools.panels.themeName || "default"; | 294   let theme = browser.devtools.panels.themeName || "default"; | 
| 351   document.body.classList.add(theme); | 295   document.body.classList.add(theme); | 
| 352 }, false); | 296 }, false); | 
| LEFT | RIGHT | 
|---|