| Index: devtools-panel.js | 
| =================================================================== | 
| --- a/devtools-panel.js | 
| +++ b/devtools-panel.js | 
| @@ -23,17 +23,12 @@ | 
| (filterTypes) => | 
| { | 
| let filterTypesElem = document.getElementById("filter-type"); | 
| - let filterStyleElem = document.createElement("style"); | 
| for (let type of filterTypes) | 
| { | 
| - filterStyleElem.innerHTML += | 
| - `#items[data-filter-type=${type}] tr:not([data-type=${type}])` + | 
| - "{display: none;}"; | 
| let optionNode = document.createElement("option"); | 
| optionNode.appendChild(document.createTextNode(type)); | 
| filterTypesElem.appendChild(optionNode); | 
| } | 
| - document.body.appendChild(filterStyleElem); | 
| }); | 
| function generateFilter(request, domainSpecific) | 
| @@ -190,10 +185,79 @@ | 
| row.classList.remove("filtered-by-search"); | 
| } | 
| +/** | 
| + * Return a generic <tr> placeholder | 
| + * double linked with the <tr> itself to simplify | 
| + * tbody swaps / filtering of undesired rows. | 
| + * @param {HTMLTableRowElement} tr | 
| + * @returns {Comment} | 
| + */ | 
| +function getPlaceholder(tr) | 
| +{ | 
| + let placeholder = tr.placeholder; | 
| + if (!placeholder) | 
| + { | 
| + placeholder = document.createComment("placeholder"); | 
| + tr.placeholder = placeholder; | 
| + placeholder.placeholder = tr; | 
| + } | 
| + return placeholder; | 
| +} | 
| + | 
| +/** | 
| + * Put back all hidden table rows and eventually | 
| + * filters those that do not match the state or type. | 
| + * @param {HTMLTableSectionElement} tbody | 
| + * @param {DOMStringMap} dataset | 
| + */ | 
| +function updateRows(tbody, dataset) | 
| +{ | 
| + const {filterState, filterType} = dataset; | 
| + | 
| + // create a list of possible CSS filter | 
| + // excluding #background-items from the list of rows to consider | 
| + const query = []; | 
| + if (filterState) | 
| + { | 
| + query.push(`tr:not(#background-items):not([data-state="${filterState}"])`); | 
| + } | 
| + if (filterType) | 
| + { | 
| + query.push(`tr:not(#background-items):not([data-type="${filterType}"])`); | 
| + } | 
| + | 
| + // use filters to match nodes or retrieve them later on | 
| + const selector = query.join(","); | 
| + for (const node of tbody.childNodes) | 
| + { | 
| + // comments linked to table rows | 
| + // that won't match selector will be used | 
| + // to place their linked row back in the tbody | 
| + if ( | 
| + node.nodeType === Node.COMMENT_NODE && | 
| + (!selector || !node.placeholder.matches(selector)) | 
| + ) | 
| + { | 
| + tbody.replaceChild(node.placeholder, node); | 
| + } | 
| + } | 
| + | 
| + // if there is a list of nodes to filter | 
| + if (selector) | 
| + { | 
| + // per each of them put a placeholder inside the tbody | 
| + for (const tr of tbody.querySelectorAll(selector)) | 
| + { | 
| + tbody.replaceChild(getPlaceholder(tr), tr); | 
| + } | 
| + } | 
| +} | 
| + | 
| document.addEventListener("DOMContentLoaded", () => | 
| { | 
| let container = document.getElementById("items"); | 
| let table = container.querySelector("tbody"); | 
| + let bgItems = container.querySelector("#background-items"); | 
| let template = document.querySelector("template").content.firstElementChild; | 
| document.getElementById("reload").addEventListener("click", () => | 
| @@ -204,11 +268,13 @@ | 
| document.getElementById("filter-state").addEventListener("change", (event) => | 
| { | 
| container.dataset.filterState = event.target.value; | 
| + updateRows(table, container.dataset); | 
| }, false); | 
| document.getElementById("filter-type").addEventListener("change", (event) => | 
| { | 
| container.dataset.filterType = event.target.value; | 
| + updateRows(table, container.dataset); | 
| }, false); | 
| ext.onMessage.addListener((message) => | 
| @@ -216,8 +282,16 @@ | 
| switch (message.type) | 
| { | 
| case "add-record": | 
| - table.appendChild(createRecord(message.request, message.filter, | 
| - template)); | 
| + let record = createRecord(message.request, message.filter, template); | 
| + // what is the official way to know if a request is from background tab? | 
| + if (/^SCRIPT|BACKGROUND$/.test(message.request.type)) | 
| + { | 
| + table.appendChild(record.cloneNode(true)); | 
| + } | 
| + else | 
| + { | 
| + table.insertBefore(record, bgItems); | 
| + } | 
| break; | 
| case "update-record": |