1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-09-18 08:52:26 +02:00

Add ability to quickly create exceptions in logger

This is a feature under development, hidden behind
a new advanced setting, `filterAuthorMode` which
default to `false`.

Ability to point-and-click to create temporary
exception filters for static extended filters (i.e.
cosmetic, scriptlet & html filters) from within
the summary pane in the logger. The button to
toggle on/off temporary exception filter is
labeled `#@#`.

The created exceptions are temporary and will be
lost when restarting uBO, or manually toggling off
the exception filters.

Creating temporary exception filters does not
cause the filter lists to reloaded, and thus there
is no overhead in creating/removing these temporary
exception filters.
This commit is contained in:
Raymond Hill 2019-09-24 17:05:03 -04:00
parent 733b2330de
commit 59c9a34d34
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
9 changed files with 203 additions and 47 deletions

View File

@ -660,6 +660,7 @@ body[dir="rtl"] #netFilteringDialog > .panes > .details > div > span:nth-of-type
border-left: 1px solid white; border-left: 1px solid white;
} }
#netFilteringDialog > .panes > .details > div > span:nth-of-type(2) { #netFilteringDialog > .panes > .details > div > span:nth-of-type(2) {
flex-grow: 1;
max-height: 20vh; max-height: 20vh;
overflow: hidden auto; overflow: hidden auto;
white-space: pre-line white-space: pre-line
@ -675,6 +676,26 @@ body[dir="rtl"] #netFilteringDialog > .panes > .details > div > span:nth-of-type
#netFilteringDialog > .panes > .details > div > span:nth-of-type(2) .fa-icon:hover { #netFilteringDialog > .panes > .details > div > span:nth-of-type(2) .fa-icon:hover {
opacity: 1; opacity: 1;
} }
#netFilteringDialog > .panes > .details .exceptor {
align-items: center;
border-left: 1px solid white;
cursor: pointer;
display: inline-flex;
font-family: monospace;
opacity: 0.8;
}
#netFilteringDialog > .panes > .details .exceptor:hover {
opacity: 1;
}
#netFilteringDialog > .panes > .details .exceptored .filter {
text-decoration: line-through;
}
#netFilteringDialog > .panes > .details .exceptored .exceptor {
background-color: lightblue;
}
#netFilteringDialog > .panes > .details .exceptor::before {
content: '#@#';
}
#netFilteringDialog > div.panes > .dynamic > .toolbar { #netFilteringDialog > div.panes > .dynamic > .toolbar {
padding-bottom: 1em; padding-bottom: 1em;
} }

View File

@ -53,6 +53,7 @@ const µBlock = (( ) => { // jshint ignore:line
extensionUpdateForceReload: false, extensionUpdateForceReload: false,
ignoreRedirectFilters: false, ignoreRedirectFilters: false,
ignoreScriptInjectFilters: false, ignoreScriptInjectFilters: false,
filterAuthorMode: false,
loggerPopupType: 'popup', loggerPopupType: 'popup',
manualUpdateAssetFetchPeriod: 500, manualUpdateAssetFetchPeriod: 500,
popupFontSize: 'unset', popupFontSize: 'unset',

View File

@ -826,6 +826,12 @@ FilterContainer.prototype.randomAlphaToken = function() {
/******************************************************************************/ /******************************************************************************/
FilterContainer.prototype.getSession = function() {
return this.specificFilters.session;
};
/******************************************************************************/
FilterContainer.prototype.retrieveGenericSelectors = function(request) { FilterContainer.prototype.retrieveGenericSelectors = function(request) {
if ( this.acceptedCount === 0 ) { return; } if ( this.acceptedCount === 0 ) { return; }
if ( !request.ids && !request.classes ) { return; } if ( !request.ids && !request.classes ) { return; }
@ -990,12 +996,15 @@ FilterContainer.prototype.retrieveSpecificSelectors = function(
} }
} }
// Retrieve temporary filters
this.specificFilters.session.retrieve([ dummySet, exceptionSet ]);
// Retrieve filters with a non-empty hostname // Retrieve filters with a non-empty hostname
this.specificFilters.retrieve( this.specificFilters.retrieve(
hostname, hostname,
options.noSpecificCosmeticFiltering !== true options.noSpecificCosmeticFiltering !== true
? [ specificSet, exceptionSet, proceduralSet, exceptionSet ] ? [ specificSet, exceptionSet, proceduralSet, exceptionSet ]
: [ dummySet, exceptionSet, dummySet, exceptionSet ], : [ dummySet, exceptionSet ],
1 1
); );
// Retrieve filters with an empty hostname // Retrieve filters with an empty hostname
@ -1003,7 +1012,7 @@ FilterContainer.prototype.retrieveSpecificSelectors = function(
hostname, hostname,
options.noGenericCosmeticFiltering !== true options.noGenericCosmeticFiltering !== true
? [ specificSet, exceptionSet, proceduralSet, exceptionSet ] ? [ specificSet, exceptionSet, proceduralSet, exceptionSet ]
: [ dummySet, exceptionSet, dummySet, exceptionSet ], : [ dummySet, exceptionSet ],
2 2
); );
// Retrieve filters with a non-empty entity // Retrieve filters with a non-empty entity
@ -1012,7 +1021,7 @@ FilterContainer.prototype.retrieveSpecificSelectors = function(
`${hostname.slice(0, -request.domain.length)}${request.entity}`, `${hostname.slice(0, -request.domain.length)}${request.entity}`,
options.noSpecificCosmeticFiltering !== true options.noSpecificCosmeticFiltering !== true
? [ specificSet, exceptionSet, proceduralSet, exceptionSet ] ? [ specificSet, exceptionSet, proceduralSet, exceptionSet ]
: [ dummySet, exceptionSet, dummySet, exceptionSet ], : [ dummySet, exceptionSet ],
1 1
); );
} }

View File

@ -334,6 +334,10 @@
} }
}; };
api.getSession = function() {
return filterDB.session;
};
api.retrieve = function(details) { api.retrieve = function(details) {
const hostname = details.hostname; const hostname = details.hostname;
@ -350,6 +354,7 @@
const procedurals = new Set(); const procedurals = new Set();
const exceptions = new Set(); const exceptions = new Set();
filterDB.session.retrieve([ new Set(), exceptions ]);
filterDB.retrieve( filterDB.retrieve(
hostname, hostname,
[ plains, exceptions, procedurals, exceptions ] [ plains, exceptions, procedurals, exceptions ]

View File

@ -41,6 +41,7 @@ let filteredLoggerEntryVoidedCount = 0;
let popupLoggerBox; let popupLoggerBox;
let popupLoggerTooltips; let popupLoggerTooltips;
let activeTabId = 0; let activeTabId = 0;
let filterAuthorMode = false;
let selectedTabId = 0; let selectedTabId = 0;
let netInspectorPaused = false; let netInspectorPaused = false;
@ -64,7 +65,7 @@ const tabIdFromAttribute = function(elem) {
// Current design allows for only one modal DOM-based dialog at any given time. // Current design allows for only one modal DOM-based dialog at any given time.
// //
const modalDialog = (function() { const modalDialog = (( ) => {
const overlay = uDom.nodeFromId('modalOverlay'); const overlay = uDom.nodeFromId('modalOverlay');
const container = overlay.querySelector( const container = overlay.querySelector(
':scope > div > div:nth-of-type(1)' ':scope > div > div:nth-of-type(1)'
@ -949,6 +950,8 @@ const onLogBufferRead = function(response) {
allTabIdsToken = response.tabIdsToken; allTabIdsToken = response.tabIdsToken;
} }
filterAuthorMode = response.filterAuthorMode === true;
if ( activeTabIdChanged ) { if ( activeTabIdChanged ) {
pageSelectorFromURLHash(); pageSelectorFromURLHash();
} }
@ -1085,7 +1088,7 @@ const reloadTab = function(ev) {
/******************************************************************************/ /******************************************************************************/
/******************************************************************************/ /******************************************************************************/
(function() { (( ) => {
const reRFC3986 = /^([^:\/?#]+:)?(\/\/[^\/?#]*)?([^?#]*)(\?[^#]*)?(#.*)?/; const reRFC3986 = /^([^:\/?#]+:)?(\/\/[^\/?#]*)?([^?#]*)(\?[^#]*)?(#.*)?/;
const reSchemeOnly = /^[\w-]+:$/; const reSchemeOnly = /^[\w-]+:$/;
const staticFilterTypes = { const staticFilterTypes = {
@ -1203,24 +1206,35 @@ const reloadTab = function(ev) {
); );
}; };
const onClick = function(ev) { const onClick = async function(ev) {
const target = ev.target; const target = ev.target;
const tcl = target.classList; const tcl = target.classList;
// Select a mode // Select a mode
if ( tcl.contains('header') ) { if ( tcl.contains('header') ) {
dialog.setAttribute('data-pane', target.getAttribute('data-pane') );
ev.stopPropagation(); ev.stopPropagation();
dialog.setAttribute('data-pane', target.getAttribute('data-pane') );
return; return;
} }
// Toggle temporary exception filter
if ( tcl.contains('exceptor') ) {
ev.stopPropagation();
const status = await messaging.send('loggerUI', {
what: 'toggleTemporaryException',
filter: filterFromTargetRow(),
});
const row = target.closest('div');
row.classList.toggle('exceptored', status);
return;
}
// Create static filter // Create static filter
if ( target.id === 'createStaticFilter' ) { if ( target.id === 'createStaticFilter' ) {
ev.stopPropagation();
const value = staticFilterNode().value; const value = staticFilterNode().value;
// Avoid duplicates // Avoid duplicates
if ( createdStaticFilters.hasOwnProperty(value) ) { if ( createdStaticFilters.hasOwnProperty(value) ) { return; }
return;
}
createdStaticFilters[value] = true; createdStaticFilters[value] = true;
if ( value !== '' ) { if ( value !== '' ) {
messaging.send('loggerUI', { messaging.send('loggerUI', {
@ -1232,21 +1246,19 @@ const reloadTab = function(ev) {
}); });
} }
updateWidgets(); updateWidgets();
ev.stopPropagation();
return; return;
} }
// Save url filtering rule(s) // Save url filtering rule(s)
if ( target.id === 'saveRules' ) { if ( target.id === 'saveRules' ) {
messaging.send('loggerUI', { ev.stopPropagation();
await messaging.send('loggerUI', {
what: 'saveURLFilteringRules', what: 'saveURLFilteringRules',
context: selectValue('select.dynamic.origin'), context: selectValue('select.dynamic.origin'),
urls: targetURLs, urls: targetURLs,
type: uglyTypeFromSelector('dynamic'), type: uglyTypeFromSelector('dynamic'),
}).then(( ) => {
colorize();
}); });
ev.stopPropagation(); colorize();
return; return;
} }
@ -1254,87 +1266,83 @@ const reloadTab = function(ev) {
// Remove url filtering rule // Remove url filtering rule
if ( tcl.contains('action') ) { if ( tcl.contains('action') ) {
messaging.send('loggerUI', { ev.stopPropagation();
await messaging.send('loggerUI', {
what: 'setURLFilteringRule', what: 'setURLFilteringRule',
context: selectValue('select.dynamic.origin'), context: selectValue('select.dynamic.origin'),
url: target.getAttribute('data-url'), url: target.getAttribute('data-url'),
type: uglyTypeFromSelector('dynamic'), type: uglyTypeFromSelector('dynamic'),
action: 0, action: 0,
persist: persist, persist: persist,
}).then(( ) => {
colorize();
}); });
ev.stopPropagation(); colorize();
return; return;
} }
// add "allow" url filtering rule // add "allow" url filtering rule
if ( tcl.contains('allow') ) { if ( tcl.contains('allow') ) {
messaging.send('loggerUI', { ev.stopPropagation();
await messaging.send('loggerUI', {
what: 'setURLFilteringRule', what: 'setURLFilteringRule',
context: selectValue('select.dynamic.origin'), context: selectValue('select.dynamic.origin'),
url: target.parentNode.getAttribute('data-url'), url: target.parentNode.getAttribute('data-url'),
type: uglyTypeFromSelector('dynamic'), type: uglyTypeFromSelector('dynamic'),
action: 2, action: 2,
persist: persist, persist: persist,
}).then(( ) => {
colorize();
}); });
ev.stopPropagation(); colorize();
return; return;
} }
// add "block" url filtering rule // add "block" url filtering rule
if ( tcl.contains('noop') ) { if ( tcl.contains('noop') ) {
messaging.send('loggerUI', { ev.stopPropagation();
await messaging.send('loggerUI', {
what: 'setURLFilteringRule', what: 'setURLFilteringRule',
context: selectValue('select.dynamic.origin'), context: selectValue('select.dynamic.origin'),
url: target.parentNode.getAttribute('data-url'), url: target.parentNode.getAttribute('data-url'),
type: uglyTypeFromSelector('dynamic'), type: uglyTypeFromSelector('dynamic'),
action: 3, action: 3,
persist: persist, persist: persist,
}).then(( ) => {
colorize();
}); });
ev.stopPropagation(); colorize();
return; return;
} }
// add "block" url filtering rule // add "block" url filtering rule
if ( tcl.contains('block') ) { if ( tcl.contains('block') ) {
messaging.send('loggerUI', { ev.stopPropagation();
await messaging.send('loggerUI', {
what: 'setURLFilteringRule', what: 'setURLFilteringRule',
context: selectValue('select.dynamic.origin'), context: selectValue('select.dynamic.origin'),
url: target.parentNode.getAttribute('data-url'), url: target.parentNode.getAttribute('data-url'),
type: uglyTypeFromSelector('dynamic'), type: uglyTypeFromSelector('dynamic'),
action: 1, action: 1,
persist: persist, persist: persist,
}).then(( ) => {
colorize();
}); });
ev.stopPropagation(); colorize();
return; return;
} }
// Force a reload of the tab // Force a reload of the tab
if ( tcl.contains('reload') ) { if ( tcl.contains('reload') ) {
ev.stopPropagation();
messaging.send('loggerUI', { messaging.send('loggerUI', {
what: 'reloadTab', what: 'reloadTab',
tabId: targetTabId, tabId: targetTabId,
}); });
ev.stopPropagation();
return; return;
} }
// Hightlight corresponding element in target web page // Hightlight corresponding element in target web page
if ( tcl.contains('picker') ) { if ( tcl.contains('picker') ) {
ev.stopPropagation();
messaging.send('loggerUI', { messaging.send('loggerUI', {
what: 'launchElementPicker', what: 'launchElementPicker',
tabId: targetTabId, tabId: targetTabId,
targetURL: 'img\t' + targetURLs[0], targetURL: 'img\t' + targetURLs[0],
select: true, select: true,
}); });
ev.stopPropagation();
return; return;
} }
}; };
@ -1426,6 +1434,37 @@ const reloadTab = function(ev) {
return urls; return urls;
}; };
const filterFromTargetRow = function() {
return targetRow.children[1].textContent;
};
const toSummaryPaneFilterNode = async function(receiver, filter) {
receiver.children[1].textContent = filter;
if ( filterAuthorMode !== true ) { return; }
const match = /#@?#/.exec(filter);
if ( match === null ) { return; }
const fragment = document.createDocumentFragment();
fragment.appendChild(document.createTextNode(match[0]));
const selector = filter.slice(match.index + match[0].length);
const span = document.createElement('span');
span.className = 'filter';
span.textContent = selector;
fragment.appendChild(span);
let isTemporaryException = false;
if ( match[0] === '#@#' ) {
isTemporaryException = await messaging.send('loggerUI', {
what: 'hasTemporaryException',
filter,
});
receiver.classList.toggle('exceptored', isTemporaryException);
}
if ( match[0] === '##' || isTemporaryException ) {
receiver.children[2].style.visibility = '';
}
receiver.children[1].textContent = '';
receiver.children[1].appendChild(fragment);
};
const fillSummaryPaneFilterList = async function(rows) { const fillSummaryPaneFilterList = async function(rows) {
const rawFilter = targetRow.children[1].textContent; const rawFilter = targetRow.children[1].textContent;
const compiledFilter = targetRow.getAttribute('data-filter'); const compiledFilter = targetRow.getAttribute('data-filter');
@ -1468,7 +1507,7 @@ const reloadTab = function(ev) {
bestMatchFilter !== '' && bestMatchFilter !== '' &&
Array.isArray(response[bestMatchFilter]) Array.isArray(response[bestMatchFilter])
) { ) {
rows[0].children[1].textContent = bestMatchFilter; toSummaryPaneFilterNode(rows[0], bestMatchFilter);
rows[1].children[1].appendChild(nodeFromFilter( rows[1].children[1].appendChild(nodeFromFilter(
bestMatchFilter, bestMatchFilter,
response[bestMatchFilter] response[bestMatchFilter]
@ -1499,7 +1538,7 @@ const reloadTab = function(ev) {
}); });
handleResponse(response); handleResponse(response);
} }
}; } ;
const fillSummaryPane = function() { const fillSummaryPane = function() {
const rows = dialog.querySelectorAll('.pane.details > div'); const rows = dialog.querySelectorAll('.pane.details > div');
@ -1508,12 +1547,12 @@ const reloadTab = function(ev) {
const trch = tr.children; const trch = tr.children;
let text; let text;
// Filter and context // Filter and context
text = trch[1].textContent; text = filterFromTargetRow();
if ( if (
(text !== '') && (text !== '') &&
(trcl.contains('cosmeticRealm') || trcl.contains('networkRealm')) (trcl.contains('cosmeticRealm') || trcl.contains('networkRealm'))
) { ) {
rows[0].children[1].textContent = text; toSummaryPaneFilterNode(rows[0], text);
} else { } else {
rows[0].style.display = 'none'; rows[0].style.display = 'none';
} }
@ -1753,7 +1792,7 @@ const reloadTab = function(ev) {
fillSummaryPane(); fillSummaryPane();
fillDynamicPane(); fillDynamicPane();
fillStaticPane(); fillStaticPane();
dialog.addEventListener('click', onClick, true); dialog.addEventListener('click', ev => { onClick(ev); }, true);
dialog.addEventListener('change', onSelectChange, true); dialog.addEventListener('change', onSelectChange, true);
dialog.addEventListener('input', onInputChange, true); dialog.addEventListener('input', onInputChange, true);
modalDialog.show(); modalDialog.show();

View File

@ -1203,10 +1203,11 @@ const extensionOriginURL = vAPI.getURL('');
const getLoggerData = async function(details, activeTabId, callback) { const getLoggerData = async function(details, activeTabId, callback) {
const response = { const response = {
activeTabId,
colorBlind: µb.userSettings.colorBlindFriendly, colorBlind: µb.userSettings.colorBlindFriendly,
entries: µb.logger.readAll(details.ownerId), entries: µb.logger.readAll(details.ownerId),
filterAuthorMode: µb.hiddenSettings.filterAuthorMode,
maxEntries: µb.userSettings.requestLogMaxEntries, maxEntries: µb.userSettings.requestLogMaxEntries,
activeTabId: activeTabId,
tabIdsToken: µb.pageStoresToken, tabIdsToken: µb.pageStoresToken,
tooltips: µb.userSettings.tooltipsDisabled === false tooltips: µb.userSettings.tooltipsDisabled === false
}; };
@ -1278,6 +1279,41 @@ const getURLFilteringData = function(details) {
return response; return response;
}; };
const compileTemporaryException = function(filter) {
const match = /#@?#/.exec(filter);
if ( match === null ) { return; }
let selector = filter.slice(match.index + match[0].length);
let session;
if ( selector.startsWith('+js') ) {
session = µb.scriptletFilteringEngine.getSession();
selector = selector.slice(4, -1).trim();
} else {
if ( selector.startsWith('^') ) {
session = µb.htmlFilteringEngine.getSession();
selector = selector.slice(1).trim();
} else {
session = µb.cosmeticFilteringEngine.getSession();
}
selector = µb.staticExtFilteringEngine.compileSelector(selector);
}
return { session, selector };
};
const toggleTemporaryException = function(details) {
const { session, selector } = compileTemporaryException(details.filter);
if ( session.has(1, selector) ) {
session.remove(1, selector);
return false;
}
session.add(1, selector);
return true;
};
const hasTemporaryException = function(details) {
const { session, selector } = compileTemporaryException(details.filter);
return session && session.has(1, selector);
};
const onMessage = function(request, sender, callback) { const onMessage = function(request, sender, callback) {
// Async // Async
switch ( request.what ) { switch ( request.what ) {
@ -1301,6 +1337,10 @@ const onMessage = function(request, sender, callback) {
let response; let response;
switch ( request.what ) { switch ( request.what ) {
case 'hasTemporaryException':
response = hasTemporaryException(request);
break;
case 'releaseView': case 'releaseView':
if ( request.ownerId === µb.logger.ownerId ) { if ( request.ownerId === µb.logger.ownerId ) {
µb.logger.ownerId = undefined; µb.logger.ownerId = undefined;
@ -1327,6 +1367,10 @@ const onMessage = function(request, sender, callback) {
response = getURLFilteringData(request); response = getURLFilteringData(request);
break; break;
case 'toggleTemporaryException':
response = toggleTemporaryException(request);
break;
default: default:
return vAPI.messaging.UNHANDLED; return vAPI.messaging.UNHANDLED;
} }

View File

@ -342,6 +342,10 @@
} }
}; };
api.getSession = function() {
return scriptletDB.session;
};
const scriptlets$ = new Set(); const scriptlets$ = new Set();
const exceptions$ = new Set(); const exceptions$ = new Set();
const scriptletToCodeMap$ = new Map(); const scriptletToCodeMap$ = new Map();
@ -367,10 +371,8 @@
scriptlets$.clear(); scriptlets$.clear();
exceptions$.clear(); exceptions$.clear();
scriptletDB.retrieve( scriptletDB.session.retrieve([ scriptlets$, exceptions$ ]);
hostname, scriptletDB.retrieve(hostname, [ scriptlets$, exceptions$ ]);
[ scriptlets$, exceptions$ ]
);
if ( request.entity !== '' ) { if ( request.entity !== '' ) {
scriptletDB.retrieve( scriptletDB.retrieve(
`${hostname.slice(0, -request.domain.length)}${request.entity}`, `${hostname.slice(0, -request.domain.length)}${request.entity}`,

View File

@ -50,7 +50,7 @@
**/ **/
µBlock.staticExtFilteringEngine = (function() { µBlock.staticExtFilteringEngine = (( ) => {
const µb = µBlock; const µb = µBlock;
const reHasUnicode = /[^\x00-\x7F]/; const reHasUnicode = /[^\x00-\x7F]/;
const reParseRegexLiteral = /^\/(.+)\/([imu]+)?$/; const reParseRegexLiteral = /^\/(.+)\/([imu]+)?$/;
@ -520,10 +520,45 @@
// Avoid heterogeneous arrays. Thus: // Avoid heterogeneous arrays. Thus:
this.hostnameSlots = []; // array of integers this.hostnameSlots = []; // array of integers
// IMPORTANT: initialize with an empty array because -0 is NOT < 0. // IMPORTANT: initialize with an empty array because -0 is NOT < 0.
this.hostnameSlotsEx = [ [] ]; // Array of arrays of integers this.hostnameSlotsEx = [ [] ]; // array of arrays of integers
// Array of strings (selectors and pseudo-selectors) // Array of strings (selectors and pseudo-selectors)
this.strSlots = []; this.strSlots = [];
this.size = 0; this.size = 0;
// Temporary set
this.session = {
collection: new Map(),
add: function(bits, s) {
const bucket = this.collection.get(bits);
if ( bucket === undefined ) {
this.collection.set(bits, new Set([ s ]));
} else {
bucket.add(s);
}
},
remove: function(bits, s) {
const bucket = this.collection.get(bits);
if ( bucket === undefined ) { return; }
bucket.delete(s);
if ( bucket.size !== 0 ) { return; }
this.collection.delete(bits);
},
retrieve(out) {
const mask = out.length - 1;
for ( const [ bits, bucket ] of this.collection ) {
for ( const s of bucket ) {
out[bits & mask].add(s);
}
}
},
has(bits, s) {
const selectors = this.collection.get(bits);
return selectors !== undefined && selectors.has(s);
},
clear() {
this.collection.clear();
},
};
if ( selfie !== undefined ) { if ( selfie !== undefined ) {
this.fromSelfie(selfie); this.fromSelfie(selfie);
} }
@ -673,7 +708,7 @@
// https://github.com/uBlockOrigin/uBlock-issues/issues/89 // https://github.com/uBlockOrigin/uBlock-issues/issues/89
// Do not discard unknown pseudo-elements. // Do not discard unknown pseudo-elements.
api.compileSelector = (function() { api.compileSelector = (( ) => {
const reAfterBeforeSelector = /^(.+?)(::?after|::?before|::[a-z-]+)$/; const reAfterBeforeSelector = /^(.+?)(::?after|::?before|::[a-z-]+)$/;
const reStyleSelector = /^(.+?):style\((.+?)\)$/; const reStyleSelector = /^(.+?):style\((.+?)\)$/;
const reExtendedSyntax = /\[-(?:abp|ext)-[a-z-]+=(['"])(?:.+?)(?:\1)\]/; const reExtendedSyntax = /\[-(?:abp|ext)-[a-z-]+=(['"])(?:.+?)(?:\1)\]/;

View File

@ -112,7 +112,7 @@
</div> </div>
<div class="panes"> <div class="panes">
<div class="pane details" data-pane="details"> <div class="pane details" data-pane="details">
<div><span data-i18n="loggerEntryDetailsFilter"></span><span></span></div> <div><span data-i18n="loggerEntryDetailsFilter"></span><span></span><span class="exceptor" style="visibility: collapse"></span></div>
<div><span data-i18n="loggerEntryDetailsFilterList"></span><span class="prose"></span></div> <div><span data-i18n="loggerEntryDetailsFilterList"></span><span class="prose"></span></div>
<div><span data-i18n="loggerEntryDetailsRule"></span><span></span></div> <div><span data-i18n="loggerEntryDetailsRule"></span><span></span></div>
<div><span data-i18n="loggerEntryDetailsRootContext"></span><span></span></div> <div><span data-i18n="loggerEntryDetailsRootContext"></span><span></span></div>