2015-05-09 00:28:01 +02:00
|
|
|
/*******************************************************************************
|
|
|
|
|
2015-06-11 18:12:23 +02:00
|
|
|
uBlock Origin - a browser extension to block requests.
|
2018-07-22 14:14:02 +02:00
|
|
|
Copyright (C) 2015-present Raymond Hill
|
2015-05-09 00:28:01 +02:00
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
|
|
|
|
2015-06-11 18:12:23 +02:00
|
|
|
Home: https://github.com/gorhill/uBlock
|
2015-05-09 00:28:01 +02:00
|
|
|
*/
|
|
|
|
|
2016-03-06 16:51:06 +01:00
|
|
|
/* global uDom */
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2018-07-22 14:14:02 +02:00
|
|
|
'use strict';
|
|
|
|
|
2015-05-09 00:28:01 +02:00
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-09-17 21:15:01 +02:00
|
|
|
(( ) => {
|
2015-05-09 00:28:01 +02:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2021-03-13 14:53:34 +01:00
|
|
|
// TODO: fix the inconsistencies re. realm vs. filter source which have
|
|
|
|
// accumulated over time.
|
|
|
|
|
2018-12-13 18:30:54 +01:00
|
|
|
const messaging = vAPI.messaging;
|
2018-12-14 17:01:21 +01:00
|
|
|
const logger = self.logger = { ownerId: Date.now() };
|
2019-01-12 22:36:20 +01:00
|
|
|
const logDate = new Date();
|
|
|
|
const logDateTimezoneOffset = logDate.getTimezoneOffset() * 60000;
|
|
|
|
const loggerEntries = [];
|
|
|
|
|
|
|
|
let filteredLoggerEntries = [];
|
|
|
|
let filteredLoggerEntryVoidedCount = 0;
|
|
|
|
|
2018-12-14 17:01:21 +01:00
|
|
|
let popupLoggerBox;
|
2018-12-17 19:54:17 +01:00
|
|
|
let popupLoggerTooltips;
|
2019-01-12 22:36:20 +01:00
|
|
|
let activeTabId = 0;
|
2019-09-24 23:05:03 +02:00
|
|
|
let filterAuthorMode = false;
|
2019-01-12 22:36:20 +01:00
|
|
|
let selectedTabId = 0;
|
2018-12-17 19:54:17 +01:00
|
|
|
let netInspectorPaused = false;
|
2019-11-19 18:05:33 +01:00
|
|
|
let cnameOfEnabled = false;
|
2015-06-28 23:42:08 +02:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
// Various helpers.
|
2015-06-28 23:42:08 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const tabIdFromPageSelector = logger.tabIdFromPageSelector = function() {
|
|
|
|
const value = uDom.nodeFromId('pageSelector').value;
|
|
|
|
return value !== '_' ? (parseInt(value, 10) || 0) : activeTabId;
|
2018-01-09 14:08:17 +01:00
|
|
|
};
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const tabIdFromAttribute = function(elem) {
|
|
|
|
const value = elem.getAttribute('data-tabid') || '';
|
|
|
|
const tabId = parseInt(value, 10);
|
|
|
|
return isNaN(tabId) ? 0 : tabId;
|
2015-06-28 23:42:08 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
// Current design allows for only one modal DOM-based dialog at any given time.
|
|
|
|
//
|
2019-09-24 23:05:03 +02:00
|
|
|
const modalDialog = (( ) => {
|
2019-01-12 22:36:20 +01:00
|
|
|
const overlay = uDom.nodeFromId('modalOverlay');
|
|
|
|
const container = overlay.querySelector(
|
|
|
|
':scope > div > div:nth-of-type(1)'
|
|
|
|
);
|
|
|
|
const closeButton = overlay.querySelector(
|
|
|
|
':scope > div > div:nth-of-type(2)'
|
|
|
|
);
|
|
|
|
let onDestroyed;
|
|
|
|
|
|
|
|
const removeChildren = logger.removeAllChildren = function(node) {
|
|
|
|
while ( node.firstChild ) {
|
|
|
|
node.removeChild(node.firstChild);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const create = function(selector, destroyListener) {
|
|
|
|
const template = document.querySelector(selector);
|
|
|
|
const dialog = template.cloneNode(true);
|
|
|
|
removeChildren(container);
|
|
|
|
container.appendChild(dialog);
|
|
|
|
onDestroyed = destroyListener;
|
|
|
|
return dialog;
|
|
|
|
};
|
|
|
|
|
|
|
|
const show = function() {
|
|
|
|
overlay.classList.add('on');
|
|
|
|
};
|
|
|
|
|
|
|
|
const destroy = function() {
|
|
|
|
overlay.classList.remove('on');
|
|
|
|
const dialog = container.firstElementChild;
|
|
|
|
removeChildren(container);
|
|
|
|
if ( typeof onDestroyed === 'function' ) {
|
|
|
|
onDestroyed(dialog);
|
|
|
|
}
|
|
|
|
onDestroyed = undefined;
|
|
|
|
};
|
|
|
|
|
|
|
|
const onClose = function(ev) {
|
|
|
|
if ( ev.target === overlay || ev.target === closeButton ) {
|
|
|
|
destroy();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
overlay.addEventListener('click', onClose);
|
|
|
|
closeButton.addEventListener('click', onClose);
|
|
|
|
|
|
|
|
return { create, show, destroy };
|
|
|
|
})();
|
|
|
|
|
|
|
|
self.logger.modalDialog = modalDialog;
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
2015-05-16 16:15:02 +02:00
|
|
|
|
2018-12-17 19:54:17 +01:00
|
|
|
const prettyRequestTypes = {
|
2015-05-09 00:28:01 +02:00
|
|
|
'main_frame': 'doc',
|
|
|
|
'stylesheet': 'css',
|
|
|
|
'sub_frame': 'frame',
|
|
|
|
'xmlhttprequest': 'xhr'
|
|
|
|
};
|
|
|
|
|
2018-12-17 19:54:17 +01:00
|
|
|
const uglyRequestTypes = {
|
2015-05-21 20:15:17 +02:00
|
|
|
'doc': 'main_frame',
|
|
|
|
'css': 'stylesheet',
|
|
|
|
'frame': 'sub_frame',
|
|
|
|
'xhr': 'xmlhttprequest'
|
|
|
|
};
|
|
|
|
|
2018-12-17 19:54:17 +01:00
|
|
|
let allTabIds = new Map();
|
|
|
|
let allTabIdsToken;
|
|
|
|
|
2015-06-26 06:08:41 +02:00
|
|
|
/******************************************************************************/
|
2015-05-25 00:50:09 +02:00
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const regexFromURLFilteringResult = function(result) {
|
|
|
|
const beg = result.indexOf(' ');
|
|
|
|
const end = result.indexOf(' ', beg + 1);
|
|
|
|
const url = result.slice(beg + 1, end);
|
2015-05-21 20:15:17 +02:00
|
|
|
if ( url === '*' ) {
|
2015-06-09 16:27:08 +02:00
|
|
|
return new RegExp('^.*$', 'gi');
|
2015-05-21 20:15:17 +02:00
|
|
|
}
|
2015-06-09 16:27:08 +02:00
|
|
|
return new RegExp('^' + url.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
|
2015-05-21 20:15:17 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2015-05-09 00:28:01 +02:00
|
|
|
// Emphasize hostname in URL, as this is what matters in uMatrix's rules.
|
|
|
|
|
2019-06-30 22:15:19 +02:00
|
|
|
const nodeFromURL = function(parent, url, re) {
|
|
|
|
const fragment = document.createDocumentFragment();
|
2019-11-23 18:46:52 +01:00
|
|
|
if ( re === undefined ) {
|
2019-06-30 22:15:19 +02:00
|
|
|
fragment.textContent = url;
|
|
|
|
} else {
|
2019-11-23 18:46:52 +01:00
|
|
|
if ( typeof re === 'string' ) {
|
|
|
|
re = new RegExp(re.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g');
|
|
|
|
}
|
2019-06-30 22:15:19 +02:00
|
|
|
const matches = re.exec(url);
|
|
|
|
if ( matches === null || matches[0].length === 0 ) {
|
|
|
|
fragment.textContent = url;
|
|
|
|
} else {
|
|
|
|
if ( matches.index !== 0 ) {
|
|
|
|
fragment.appendChild(
|
|
|
|
document.createTextNode(url.slice(0, matches.index))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
const b = document.createElement('b');
|
|
|
|
b.textContent = url.slice(matches.index, re.lastIndex);
|
|
|
|
fragment.appendChild(b);
|
|
|
|
if ( re.lastIndex !== url.length ) {
|
|
|
|
fragment.appendChild(
|
|
|
|
document.createTextNode(url.slice(re.lastIndex))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2015-05-09 00:28:01 +02:00
|
|
|
}
|
2019-06-30 22:15:19 +02:00
|
|
|
if ( /^https?:\/\//.test(url) ) {
|
|
|
|
const a = document.createElement('a');
|
|
|
|
a.setAttribute('href', url);
|
|
|
|
a.setAttribute('target', '_blank');
|
|
|
|
fragment.appendChild(a);
|
2015-05-09 00:28:01 +02:00
|
|
|
}
|
2019-06-30 22:15:19 +02:00
|
|
|
parent.appendChild(fragment);
|
2015-05-09 00:28:01 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const padTo2 = function(v) {
|
|
|
|
return v < 10 ? '0' + v : v;
|
|
|
|
};
|
|
|
|
|
|
|
|
const normalizeToStr = function(s) {
|
|
|
|
return typeof s === 'string' && s !== '' ? s : '';
|
2015-05-09 00:28:01 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const LogEntry = function(details) {
|
|
|
|
if ( details instanceof Object === false ) { return; }
|
|
|
|
const receiver = LogEntry.prototype;
|
|
|
|
for ( const prop in receiver ) {
|
|
|
|
if (
|
|
|
|
details.hasOwnProperty(prop) &&
|
|
|
|
details[prop] !== receiver[prop]
|
|
|
|
) {
|
|
|
|
this[prop] = details[prop];
|
2015-05-09 00:28:01 +02:00
|
|
|
}
|
|
|
|
}
|
2020-11-17 17:18:59 +01:00
|
|
|
this.type = details.stype || details.type;
|
2019-11-23 18:46:52 +01:00
|
|
|
if ( details.aliasURL !== undefined ) {
|
|
|
|
this.aliased = true;
|
|
|
|
}
|
2019-01-15 21:12:47 +01:00
|
|
|
if ( this.tabDomain === '' ) {
|
2019-01-22 12:13:30 +01:00
|
|
|
this.tabDomain = this.tabHostname || '';
|
2019-01-15 21:12:47 +01:00
|
|
|
}
|
|
|
|
if ( this.docDomain === '' ) {
|
2019-01-22 12:13:30 +01:00
|
|
|
this.docDomain = this.docHostname || '';
|
2019-01-15 21:12:47 +01:00
|
|
|
}
|
|
|
|
if ( this.domain === '' ) {
|
2019-01-22 12:13:30 +01:00
|
|
|
this.domain = details.hostname || '';
|
2019-01-15 21:12:47 +01:00
|
|
|
}
|
2019-01-12 22:36:20 +01:00
|
|
|
};
|
|
|
|
LogEntry.prototype = {
|
2019-11-23 18:46:52 +01:00
|
|
|
aliased: false,
|
2019-01-12 22:36:20 +01:00
|
|
|
dead: false,
|
|
|
|
docDomain: '',
|
|
|
|
docHostname: '',
|
|
|
|
domain: '',
|
|
|
|
filter: undefined,
|
2019-11-23 18:46:52 +01:00
|
|
|
id: '',
|
2019-01-12 22:36:20 +01:00
|
|
|
realm: '',
|
|
|
|
tabDomain: '',
|
|
|
|
tabHostname: '',
|
|
|
|
tabId: undefined,
|
|
|
|
textContent: '',
|
|
|
|
tstamp: 0,
|
|
|
|
type: '',
|
|
|
|
voided: false,
|
2015-05-09 00:28:01 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const createLogSeparator = function(details, text) {
|
|
|
|
const separator = new LogEntry();
|
|
|
|
separator.tstamp = details.tstamp;
|
|
|
|
separator.realm = 'message';
|
|
|
|
separator.tabId = details.tabId;
|
|
|
|
separator.type = 'tabLoad';
|
|
|
|
separator.textContent = '';
|
|
|
|
|
|
|
|
const textContent = [];
|
|
|
|
logDate.setTime(separator.tstamp - logDateTimezoneOffset);
|
|
|
|
textContent.push(
|
|
|
|
// cell 0
|
|
|
|
padTo2(logDate.getUTCHours()) + ':' +
|
|
|
|
padTo2(logDate.getUTCMinutes()) + ':' +
|
|
|
|
padTo2(logDate.getSeconds()),
|
|
|
|
// cell 1
|
|
|
|
text
|
|
|
|
);
|
|
|
|
separator.textContent = textContent.join('\t');
|
|
|
|
|
|
|
|
if ( details.voided ) {
|
|
|
|
separator.voided = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return separator;
|
2016-12-02 18:15:54 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
// TODO: once refactoring is mature, consider using push() instead of
|
|
|
|
// unshift(). This will require inverting the access logic
|
|
|
|
// throughout the code.
|
|
|
|
//
|
|
|
|
const processLoggerEntries = function(response) {
|
|
|
|
const entries = response.entries;
|
|
|
|
if ( entries.length === 0 ) { return; }
|
|
|
|
|
|
|
|
const autoDeleteVoidedRows = uDom.nodeFromId('pageSelector').value === '_';
|
|
|
|
const previousCount = filteredLoggerEntries.length;
|
|
|
|
|
|
|
|
for ( const entry of entries ) {
|
|
|
|
const unboxed = JSON.parse(entry);
|
2019-05-24 17:18:39 +02:00
|
|
|
if ( unboxed.filter instanceof Object ){
|
|
|
|
loggerStats.processFilter(unboxed.filter);
|
|
|
|
}
|
|
|
|
if ( netInspectorPaused ) { continue; }
|
2019-01-12 22:36:20 +01:00
|
|
|
const parsed = parseLogEntry(unboxed);
|
|
|
|
if (
|
|
|
|
parsed.tabId !== undefined &&
|
|
|
|
allTabIds.has(parsed.tabId) === false
|
|
|
|
) {
|
|
|
|
if ( autoDeleteVoidedRows ) { continue; }
|
|
|
|
parsed.voided = true;
|
|
|
|
}
|
2020-11-03 15:15:26 +01:00
|
|
|
if (
|
|
|
|
parsed.type === 'main_frame' &&
|
|
|
|
parsed.aliased === false && (
|
|
|
|
parsed.filter === undefined ||
|
|
|
|
parsed.filter.source !== 'redirect'
|
|
|
|
)
|
|
|
|
) {
|
2019-01-12 22:36:20 +01:00
|
|
|
const separator = createLogSeparator(parsed, unboxed.url);
|
|
|
|
loggerEntries.unshift(separator);
|
|
|
|
if ( rowFilterer.filterOne(separator) ) {
|
|
|
|
filteredLoggerEntries.unshift(separator);
|
|
|
|
if ( separator.voided ) {
|
|
|
|
filteredLoggerEntryVoidedCount += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-11-23 18:46:52 +01:00
|
|
|
if ( cnameOfEnabled === false && parsed.aliased ) {
|
2019-11-19 18:05:33 +01:00
|
|
|
uDom.nodeFromId('filterExprCnameOf').style.display = '';
|
|
|
|
cnameOfEnabled = true;
|
|
|
|
}
|
2019-01-12 22:36:20 +01:00
|
|
|
loggerEntries.unshift(parsed);
|
|
|
|
if ( rowFilterer.filterOne(parsed) ) {
|
|
|
|
filteredLoggerEntries.unshift(parsed);
|
|
|
|
if ( parsed.voided ) {
|
|
|
|
filteredLoggerEntryVoidedCount += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const addedCount = filteredLoggerEntries.length - previousCount;
|
|
|
|
if ( addedCount !== 0 ) {
|
|
|
|
viewPort.updateContent(addedCount);
|
|
|
|
rowJanitor.inserted(addedCount);
|
|
|
|
}
|
2015-05-09 00:28:01 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const parseLogEntry = function(details) {
|
2021-03-13 14:53:34 +01:00
|
|
|
// Patch realm until changed all over codebase to make this unecessary
|
|
|
|
if ( details.realm === 'cosmetic' ) {
|
|
|
|
details.realm = 'extended';
|
|
|
|
}
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const entry = new LogEntry(details);
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
// Assemble the text content, i.e. the pre-built string which will be
|
|
|
|
// used to match logger output filtering expressions.
|
|
|
|
const textContent = [];
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
// Cell 0
|
|
|
|
logDate.setTime(details.tstamp - logDateTimezoneOffset);
|
|
|
|
textContent.push(
|
|
|
|
padTo2(logDate.getUTCHours()) + ':' +
|
|
|
|
padTo2(logDate.getUTCMinutes()) + ':' +
|
|
|
|
padTo2(logDate.getSeconds())
|
|
|
|
);
|
2015-05-21 20:15:17 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
// Cell 1
|
|
|
|
if ( details.realm === 'message' ) {
|
|
|
|
textContent.push(details.text);
|
|
|
|
entry.textContent = textContent.join('\t');
|
|
|
|
return entry;
|
2015-05-09 00:28:01 +02:00
|
|
|
}
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
// Cell 1, 2
|
|
|
|
if ( entry.filter !== undefined ) {
|
|
|
|
textContent.push(entry.filter.raw);
|
|
|
|
if ( entry.filter.result === 1 ) {
|
|
|
|
textContent.push('--');
|
|
|
|
} else if ( entry.filter.result === 2 ) {
|
|
|
|
textContent.push('++');
|
|
|
|
} else if ( entry.filter.result === 3 ) {
|
|
|
|
textContent.push('**');
|
|
|
|
} else if ( entry.filter.source === 'redirect' ) {
|
|
|
|
textContent.push('<<');
|
2015-06-09 16:27:08 +02:00
|
|
|
} else {
|
2019-01-12 22:36:20 +01:00
|
|
|
textContent.push('');
|
2017-05-12 16:35:11 +02:00
|
|
|
}
|
2019-01-12 22:36:20 +01:00
|
|
|
} else {
|
|
|
|
textContent.push('', '');
|
2015-05-09 00:28:01 +02:00
|
|
|
}
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
// Cell 3
|
|
|
|
textContent.push(normalizeToStr(entry.docHostname));
|
2018-12-13 18:30:54 +01:00
|
|
|
|
2020-01-31 19:48:17 +01:00
|
|
|
// Cell 4: partyness
|
|
|
|
if (
|
|
|
|
entry.realm === 'network' &&
|
|
|
|
typeof entry.domain === 'string' &&
|
|
|
|
entry.domain !== ''
|
|
|
|
) {
|
|
|
|
let partyness = '';
|
|
|
|
if ( entry.tabDomain !== undefined ) {
|
|
|
|
if ( entry.tabId < 0 ) {
|
|
|
|
partyness += '0,';
|
|
|
|
}
|
|
|
|
partyness += entry.domain === entry.tabDomain ? '1' : '3';
|
|
|
|
} else {
|
|
|
|
partyness += '?';
|
|
|
|
}
|
|
|
|
if ( entry.docDomain !== entry.tabDomain ) {
|
|
|
|
partyness += ',';
|
|
|
|
if ( entry.docDomain !== undefined ) {
|
|
|
|
partyness += entry.domain === entry.docDomain ? '1' : '3';
|
2018-12-13 18:30:54 +01:00
|
|
|
} else {
|
2019-01-12 22:36:20 +01:00
|
|
|
partyness += '?';
|
2018-12-13 18:30:54 +01:00
|
|
|
}
|
|
|
|
}
|
2020-01-31 19:48:17 +01:00
|
|
|
textContent.push(partyness);
|
2019-01-12 22:36:20 +01:00
|
|
|
} else {
|
|
|
|
textContent.push('');
|
2018-12-13 18:30:54 +01:00
|
|
|
}
|
2015-06-09 16:27:08 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
// Cell 5
|
|
|
|
textContent.push(
|
|
|
|
normalizeToStr(prettyRequestTypes[entry.type] || entry.type)
|
|
|
|
);
|
2018-12-13 18:30:54 +01:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
// Cell 6
|
|
|
|
textContent.push(normalizeToStr(details.url));
|
|
|
|
|
2019-11-19 18:05:33 +01:00
|
|
|
// Hidden cells -- useful for row-filtering purpose
|
2019-11-23 18:46:52 +01:00
|
|
|
|
|
|
|
// Cell 7
|
|
|
|
if ( entry.aliased ) {
|
|
|
|
textContent.push(`aliasURL=${details.aliasURL}`);
|
2019-11-19 18:05:33 +01:00
|
|
|
}
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
entry.textContent = textContent.join('\t');
|
|
|
|
return entry;
|
2015-05-09 00:28:01 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-05-24 17:18:39 +02:00
|
|
|
const viewPort = (( ) => {
|
2019-01-12 22:36:20 +01:00
|
|
|
const vwRenderer = document.getElementById('vwRenderer');
|
|
|
|
const vwScroller = document.getElementById('vwScroller');
|
|
|
|
const vwVirtualContent = document.getElementById('vwVirtualContent');
|
|
|
|
const vwContent = document.getElementById('vwContent');
|
|
|
|
const vwLineSizer = document.getElementById('vwLineSizer');
|
|
|
|
const vwLogEntryTemplate = document.querySelector('#logEntryTemplate > div');
|
|
|
|
const vwEntries = [];
|
|
|
|
|
2021-03-13 14:53:34 +01:00
|
|
|
const detailableRealms = new Set([ 'network', 'extended' ]);
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
let vwHeight = 0;
|
|
|
|
let lineHeight = 0;
|
|
|
|
let wholeHeight = 0;
|
|
|
|
let lastTopPix = 0;
|
|
|
|
let lastTopRow = 0;
|
|
|
|
let scrollTimer;
|
|
|
|
let resizeTimer;
|
|
|
|
|
|
|
|
const ViewEntry = function() {
|
|
|
|
this.div = document.createElement('div');
|
|
|
|
this.div.className = 'logEntry';
|
|
|
|
vwContent.appendChild(this.div);
|
|
|
|
this.logEntry = undefined;
|
|
|
|
};
|
|
|
|
ViewEntry.prototype = {
|
|
|
|
dispose: function() {
|
|
|
|
vwContent.removeChild(this.div);
|
|
|
|
},
|
|
|
|
};
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const rowFromScrollTopPix = function(px) {
|
|
|
|
return lineHeight !== 0 ? Math.floor(px / lineHeight) : 0;
|
|
|
|
};
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
// This is called when the browser fired scroll events
|
|
|
|
const onScrollChanged = function() {
|
|
|
|
const newScrollTopPix = vwScroller.scrollTop;
|
|
|
|
const delta = newScrollTopPix - lastTopPix;
|
|
|
|
if ( delta === 0 ) { return; }
|
|
|
|
lastTopPix = newScrollTopPix;
|
|
|
|
if ( filteredLoggerEntries.length <= 2 ) { return; }
|
|
|
|
// No entries were rolled = all entries keep their current details
|
|
|
|
if ( rollLines(rowFromScrollTopPix(newScrollTopPix)) ) {
|
|
|
|
fillLines();
|
|
|
|
}
|
|
|
|
positionLines();
|
|
|
|
vwContent.style.top = `${lastTopPix}px`;
|
|
|
|
};
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
// Coallesce scroll events
|
|
|
|
const onScroll = function() {
|
|
|
|
if ( scrollTimer !== undefined ) { return; }
|
|
|
|
scrollTimer = setTimeout(
|
|
|
|
( ) => {
|
|
|
|
scrollTimer = requestAnimationFrame(( ) => {
|
|
|
|
scrollTimer = undefined;
|
|
|
|
onScrollChanged();
|
|
|
|
});
|
|
|
|
},
|
|
|
|
1000/32
|
|
|
|
);
|
|
|
|
};
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
vwScroller.addEventListener('scroll', onScroll, { passive: true });
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const onLayoutChanged = function() {
|
|
|
|
vwHeight = vwRenderer.clientHeight;
|
|
|
|
vwContent.style.height = `${vwScroller.clientHeight}px`;
|
2016-12-03 15:21:31 +01:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const vExpanded =
|
|
|
|
uDom.nodeFromSelector('#netInspector .vCompactToggler')
|
|
|
|
.classList
|
|
|
|
.contains('vExpanded');
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
let newLineHeight =
|
|
|
|
vwLineSizer.querySelector('.oneLine').clientHeight;
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
if ( vExpanded ) {
|
|
|
|
newLineHeight *= loggerSettings.linesPerEntry;
|
|
|
|
}
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const lineCount = newLineHeight !== 0
|
|
|
|
? Math.ceil(vwHeight / newLineHeight) + 1
|
|
|
|
: 0;
|
|
|
|
if ( lineCount > vwEntries.length ) {
|
|
|
|
do {
|
|
|
|
vwEntries.push(new ViewEntry());
|
|
|
|
} while ( lineCount > vwEntries.length );
|
|
|
|
} else if ( lineCount < vwEntries.length ) {
|
|
|
|
do {
|
|
|
|
vwEntries.pop().dispose();
|
|
|
|
} while ( lineCount < vwEntries.length );
|
|
|
|
}
|
2018-05-27 14:31:17 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const cellWidths = Array.from(
|
|
|
|
vwLineSizer.querySelectorAll('.oneLine span')
|
|
|
|
).map((el, i) => {
|
|
|
|
return loggerSettings.columns[i] !== false
|
|
|
|
? el.clientWidth + 1
|
|
|
|
: 0;
|
|
|
|
});
|
|
|
|
const reservedWidth =
|
|
|
|
cellWidths[0] + cellWidths[2] + cellWidths[4] + cellWidths[5];
|
|
|
|
cellWidths[6] = 0.5;
|
|
|
|
if ( cellWidths[1] === 0 && cellWidths[3] === 0 ) {
|
|
|
|
cellWidths[6] = 1;
|
|
|
|
} else if ( cellWidths[1] === 0 ) {
|
|
|
|
cellWidths[3] = 0.35;
|
|
|
|
cellWidths[6] = 0.65;
|
|
|
|
} else if ( cellWidths[3] === 0 ) {
|
|
|
|
cellWidths[1] = 0.35;
|
|
|
|
cellWidths[6] = 0.65;
|
|
|
|
} else {
|
|
|
|
cellWidths[1] = 0.25;
|
|
|
|
cellWidths[3] = 0.25;
|
|
|
|
cellWidths[6] = 0.5;
|
|
|
|
}
|
|
|
|
const style = document.getElementById('vwRendererRuntimeStyles');
|
|
|
|
const cssRules = [
|
|
|
|
'#vwContent .logEntry {',
|
|
|
|
` height: ${newLineHeight}px;`,
|
|
|
|
'}',
|
|
|
|
'#vwContent .logEntry > div > span:nth-of-type(1) {',
|
|
|
|
` width: ${cellWidths[0]}px;`,
|
|
|
|
'}',
|
|
|
|
'#vwContent .logEntry > div > span:nth-of-type(2) {',
|
|
|
|
` width: calc(calc(100% - ${reservedWidth}px) * ${cellWidths[1]});`,
|
|
|
|
'}',
|
|
|
|
'#vwContent .logEntry > div.messageRealm > span:nth-of-type(2) {',
|
|
|
|
` width: calc(100% - ${cellWidths[0]}px);`,
|
|
|
|
'}',
|
|
|
|
'#vwContent .logEntry > div > span:nth-of-type(3) {',
|
|
|
|
` width: ${cellWidths[2]}px;`,
|
|
|
|
'}',
|
|
|
|
'#vwContent .logEntry > div > span:nth-of-type(4) {',
|
|
|
|
` width: calc(calc(100% - ${reservedWidth}px) * ${cellWidths[3]});`,
|
|
|
|
'}',
|
|
|
|
'#vwContent .logEntry > div > span:nth-of-type(5) {',
|
|
|
|
` width: ${cellWidths[4]}px;`,
|
|
|
|
'}',
|
|
|
|
'#vwContent .logEntry > div > span:nth-of-type(6) {',
|
|
|
|
` width: ${cellWidths[5]}px;`,
|
|
|
|
'}',
|
|
|
|
'#vwContent .logEntry > div > span:nth-of-type(7) {',
|
|
|
|
` width: calc(calc(100% - ${reservedWidth}px) * ${cellWidths[6]});`,
|
|
|
|
'}',
|
|
|
|
'',
|
|
|
|
];
|
|
|
|
for ( let i = 0; i < cellWidths.length; i++ ) {
|
|
|
|
if ( cellWidths[i] !== 0 ) { continue; }
|
|
|
|
cssRules.push(
|
|
|
|
`#vwContent .logEntry > div > span:nth-of-type(${i + 1}) {`,
|
|
|
|
' display: none;',
|
|
|
|
'}'
|
|
|
|
);
|
2015-05-09 00:28:01 +02:00
|
|
|
}
|
2019-01-12 22:36:20 +01:00
|
|
|
style.textContent = cssRules.join('\n');
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
lineHeight = newLineHeight;
|
|
|
|
positionLines();
|
|
|
|
uDom.nodeFromId('netInspector')
|
|
|
|
.classList
|
|
|
|
.toggle('vExpanded', vExpanded);
|
|
|
|
|
|
|
|
updateContent(0);
|
|
|
|
};
|
|
|
|
|
|
|
|
const updateLayout = function() {
|
|
|
|
if ( resizeTimer !== undefined ) { return; }
|
|
|
|
resizeTimer = setTimeout(
|
|
|
|
( ) => {
|
|
|
|
resizeTimer = requestAnimationFrame(( ) => {
|
|
|
|
resizeTimer = undefined;
|
|
|
|
onLayoutChanged();
|
|
|
|
});
|
|
|
|
},
|
|
|
|
1000/8
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
window.addEventListener('resize', updateLayout, { passive: true });
|
|
|
|
|
|
|
|
updateLayout();
|
|
|
|
|
2019-05-17 17:45:07 +02:00
|
|
|
const renderFilterToSpan = function(span, filter) {
|
|
|
|
if ( filter.charCodeAt(0) !== 0x23 /* '#' */ ) { return false; }
|
|
|
|
const match = /^#@?#/.exec(filter);
|
|
|
|
if ( match === null ) { return false; }
|
|
|
|
let child = document.createElement('span');
|
|
|
|
child.textContent = match[0];
|
|
|
|
span.appendChild(child);
|
|
|
|
child = document.createElement('span');
|
|
|
|
child.textContent = filter.slice(match[0].length);
|
|
|
|
span.appendChild(child);
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const renderToDiv = function(vwEntry, i) {
|
|
|
|
if ( i >= filteredLoggerEntries.length ) {
|
|
|
|
vwEntry.logEntry = undefined;
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
const details = filteredLoggerEntries[i];
|
|
|
|
if ( vwEntry.logEntry === details ) {
|
|
|
|
return vwEntry.div.firstElementChild;
|
|
|
|
}
|
|
|
|
|
|
|
|
vwEntry.logEntry = details;
|
|
|
|
|
|
|
|
const cells = details.textContent.split('\t');
|
|
|
|
const div = vwLogEntryTemplate.cloneNode(true);
|
|
|
|
const divcl = div.classList;
|
|
|
|
let span;
|
|
|
|
|
|
|
|
// Realm
|
|
|
|
if ( details.realm !== undefined ) {
|
|
|
|
divcl.add(details.realm + 'Realm');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Timestamp
|
|
|
|
span = div.children[0];
|
|
|
|
span.textContent = cells[0];
|
|
|
|
|
|
|
|
// Tab id
|
|
|
|
if ( details.tabId !== undefined ) {
|
|
|
|
div.setAttribute('data-tabid', details.tabId);
|
|
|
|
if ( details.voided ) {
|
|
|
|
divcl.add('voided');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( details.realm === 'message' ) {
|
|
|
|
if ( details.type !== undefined ) {
|
|
|
|
div.setAttribute('data-type', details.type);
|
|
|
|
}
|
|
|
|
span = div.children[1];
|
|
|
|
span.textContent = cells[1];
|
|
|
|
return div;
|
|
|
|
}
|
|
|
|
|
2021-03-13 14:53:34 +01:00
|
|
|
if ( detailableRealms.has(details.realm) ) {
|
2019-01-12 22:36:20 +01:00
|
|
|
divcl.add('canDetails');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Filter
|
|
|
|
const filter = details.filter || undefined;
|
|
|
|
let filteringType;
|
|
|
|
if ( filter !== undefined ) {
|
|
|
|
if ( typeof filter.source === 'string' ) {
|
|
|
|
filteringType = filter.source;
|
|
|
|
}
|
|
|
|
if ( filteringType === 'static' ) {
|
|
|
|
divcl.add('canLookup');
|
2021-03-13 14:53:34 +01:00
|
|
|
} else if ( details.realm === 'extended' ) {
|
2019-01-12 22:36:20 +01:00
|
|
|
divcl.add('canLookup');
|
2019-05-16 19:44:49 +02:00
|
|
|
divcl.toggle('isException', filter.raw.startsWith('#@#'));
|
2019-01-12 22:36:20 +01:00
|
|
|
}
|
2021-03-13 14:53:34 +01:00
|
|
|
if ( filter.modifier === true ) {
|
|
|
|
div.setAttribute('data-modifier', '');
|
|
|
|
}
|
2019-01-12 22:36:20 +01:00
|
|
|
}
|
|
|
|
span = div.children[1];
|
2019-05-17 17:45:07 +02:00
|
|
|
if ( renderFilterToSpan(span, cells[1]) === false ) {
|
|
|
|
span.textContent = cells[1];
|
|
|
|
}
|
2019-01-12 22:36:20 +01:00
|
|
|
|
|
|
|
// Event
|
|
|
|
if ( cells[2] === '--' ) {
|
2019-01-13 14:34:17 +01:00
|
|
|
div.setAttribute('data-status', '1');
|
2019-01-12 22:36:20 +01:00
|
|
|
} else if ( cells[2] === '++' ) {
|
2019-01-13 14:34:17 +01:00
|
|
|
div.setAttribute('data-status', '2');
|
2019-01-12 22:36:20 +01:00
|
|
|
} else if ( cells[2] === '**' ) {
|
2019-01-13 14:34:17 +01:00
|
|
|
div.setAttribute('data-status', '3');
|
2019-01-12 22:36:20 +01:00
|
|
|
} else if ( cells[2] === '<<' ) {
|
|
|
|
divcl.add('redirect');
|
|
|
|
}
|
|
|
|
span = div.children[2];
|
|
|
|
span.textContent = cells[2];
|
|
|
|
|
|
|
|
// Origins
|
|
|
|
if ( details.tabHostname ) {
|
|
|
|
div.setAttribute('data-tabhn', details.tabHostname);
|
|
|
|
}
|
|
|
|
if ( details.docHostname ) {
|
|
|
|
div.setAttribute('data-dochn', details.docHostname);
|
|
|
|
}
|
|
|
|
span = div.children[3];
|
|
|
|
span.textContent = cells[3];
|
|
|
|
|
|
|
|
// Partyness
|
|
|
|
if (
|
|
|
|
cells[4] !== '' &&
|
|
|
|
details.realm === 'network' &&
|
|
|
|
details.domain !== undefined
|
|
|
|
) {
|
|
|
|
let text = `${details.tabDomain}`;
|
|
|
|
if ( details.docDomain !== details.tabDomain ) {
|
|
|
|
text += ` \u22ef ${details.docDomain}`;
|
|
|
|
}
|
|
|
|
text += ` \u21d2 ${details.domain}`;
|
|
|
|
div.setAttribute('data-parties', text);
|
|
|
|
}
|
|
|
|
span = div.children[4];
|
|
|
|
span.textContent = cells[4];
|
|
|
|
|
|
|
|
// Type
|
|
|
|
span = div.children[5];
|
|
|
|
span.textContent = cells[5];
|
|
|
|
|
|
|
|
// URL
|
2019-11-23 18:46:52 +01:00
|
|
|
let re;
|
2019-01-12 22:36:20 +01:00
|
|
|
if ( filteringType === 'static' ) {
|
|
|
|
re = new RegExp(filter.regex, 'gi');
|
|
|
|
} else if ( filteringType === 'dynamicUrl' ) {
|
|
|
|
re = regexFromURLFilteringResult(filter.rule.join(' '));
|
|
|
|
}
|
2019-06-30 22:15:19 +02:00
|
|
|
nodeFromURL(div.children[6], cells[6], re);
|
2019-01-12 22:36:20 +01:00
|
|
|
|
2019-11-23 18:46:52 +01:00
|
|
|
// Alias URL (CNAME, etc.)
|
|
|
|
if ( cells.length > 7 ) {
|
|
|
|
const pos = details.textContent.lastIndexOf('\taliasURL=');
|
|
|
|
if ( pos !== -1 ) {
|
|
|
|
div.setAttribute('data-aliasid', details.id);
|
|
|
|
}
|
2019-11-19 18:05:33 +01:00
|
|
|
}
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
return div;
|
|
|
|
};
|
|
|
|
|
|
|
|
// The idea is that positioning DOM elements is faster than
|
|
|
|
// removing/inserting DOM elements.
|
|
|
|
const positionLines = function() {
|
|
|
|
if ( lineHeight === 0 ) { return; }
|
|
|
|
let y = -(lastTopPix % lineHeight);
|
|
|
|
for ( const vwEntry of vwEntries ) {
|
|
|
|
vwEntry.div.style.top = `${y}px`;
|
|
|
|
y += lineHeight;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const rollLines = function(topRow) {
|
|
|
|
let delta = topRow - lastTopRow;
|
|
|
|
let deltaLength = Math.abs(delta);
|
|
|
|
// No point rolling if no rows can be reused
|
|
|
|
if ( deltaLength > 0 && deltaLength < vwEntries.length ) {
|
|
|
|
if ( delta < 0 ) { // Move bottom rows to the top
|
|
|
|
vwEntries.unshift(...vwEntries.splice(delta));
|
|
|
|
} else { // Move top rows to the bottom
|
|
|
|
vwEntries.push(...vwEntries.splice(0, delta));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lastTopRow = topRow;
|
|
|
|
return delta;
|
|
|
|
};
|
|
|
|
|
|
|
|
const fillLines = function() {
|
|
|
|
let rowBeg = lastTopRow;
|
|
|
|
for ( const vwEntry of vwEntries ) {
|
|
|
|
const newDiv = renderToDiv(vwEntry, rowBeg);
|
|
|
|
const container = vwEntry.div;
|
|
|
|
const oldDiv = container.firstElementChild;
|
|
|
|
if ( newDiv !== null ) {
|
|
|
|
if ( oldDiv === null ) {
|
|
|
|
container.appendChild(newDiv);
|
|
|
|
} else if ( newDiv !== oldDiv ) {
|
|
|
|
container.removeChild(oldDiv);
|
|
|
|
container.appendChild(newDiv);
|
|
|
|
}
|
|
|
|
} else if ( oldDiv !== null ) {
|
|
|
|
container.removeChild(oldDiv);
|
|
|
|
}
|
|
|
|
rowBeg += 1;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const contentChanged = function(addedCount) {
|
|
|
|
lastTopRow += addedCount;
|
|
|
|
const newWholeHeight = Math.max(
|
|
|
|
filteredLoggerEntries.length * lineHeight,
|
|
|
|
vwRenderer.clientHeight
|
|
|
|
);
|
|
|
|
if ( newWholeHeight !== wholeHeight ) {
|
|
|
|
vwVirtualContent.style.height = `${newWholeHeight}px`;
|
|
|
|
wholeHeight = newWholeHeight;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const updateContent = function(addedCount) {
|
|
|
|
contentChanged(addedCount);
|
|
|
|
// Content changed
|
|
|
|
if ( addedCount === 0 ) {
|
|
|
|
if (
|
|
|
|
lastTopRow !== 0 &&
|
|
|
|
lastTopRow + vwEntries.length > filteredLoggerEntries.length
|
|
|
|
) {
|
|
|
|
lastTopRow = filteredLoggerEntries.length - vwEntries.length;
|
|
|
|
if ( lastTopRow < 0 ) { lastTopRow = 0; }
|
|
|
|
lastTopPix = lastTopRow * lineHeight;
|
|
|
|
vwContent.style.top = `${lastTopPix}px`;
|
|
|
|
vwScroller.scrollTop = lastTopPix;
|
|
|
|
positionLines();
|
|
|
|
}
|
|
|
|
fillLines();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Content added
|
|
|
|
// Preserve scroll position
|
|
|
|
if ( lastTopPix === 0 ) {
|
|
|
|
rollLines(0);
|
|
|
|
positionLines();
|
|
|
|
fillLines();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Preserve row position
|
|
|
|
lastTopPix += lineHeight * addedCount;
|
|
|
|
vwContent.style.top = `${lastTopPix}px`;
|
|
|
|
vwScroller.scrollTop = lastTopPix;
|
|
|
|
};
|
|
|
|
|
|
|
|
return { updateContent, updateLayout, };
|
|
|
|
})();
|
2015-05-09 00:28:01 +02:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-05-24 17:18:39 +02:00
|
|
|
const updateCurrentTabTitle = (( ) => {
|
2019-01-12 22:36:20 +01:00
|
|
|
const i18nCurrentTab = vAPI.i18n('loggerCurrentTab');
|
2018-05-27 14:31:17 +02:00
|
|
|
|
|
|
|
return function() {
|
2019-01-12 22:36:20 +01:00
|
|
|
const select = uDom.nodeFromId('pageSelector');
|
|
|
|
if ( select.value !== '_' || activeTabId === 0 ) { return; }
|
|
|
|
const opt0 = select.querySelector('[value="_"]');
|
2019-05-24 17:18:39 +02:00
|
|
|
const opt1 = select.querySelector(`[value="${activeTabId}"]`);
|
2018-05-27 14:31:17 +02:00
|
|
|
let text = i18nCurrentTab;
|
|
|
|
if ( opt1 !== null ) {
|
|
|
|
text += ' / ' + opt1.textContent;
|
|
|
|
}
|
|
|
|
opt0.textContent = text;
|
|
|
|
};
|
|
|
|
})();
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2018-12-13 18:30:54 +01:00
|
|
|
const synchronizeTabIds = function(newTabIds) {
|
|
|
|
const select = uDom.nodeFromId('pageSelector');
|
2019-01-12 22:36:20 +01:00
|
|
|
const selectedTabValue = select.value;
|
2018-12-13 18:30:54 +01:00
|
|
|
const oldTabIds = allTabIds;
|
2019-01-12 22:36:20 +01:00
|
|
|
|
|
|
|
// Collate removed tab ids.
|
|
|
|
const toVoid = new Set();
|
2018-12-13 18:30:54 +01:00
|
|
|
for ( const tabId of oldTabIds.keys() ) {
|
2018-02-26 19:59:16 +01:00
|
|
|
if ( newTabIds.has(tabId) ) { continue; }
|
2019-01-12 22:36:20 +01:00
|
|
|
toVoid.add(tabId);
|
|
|
|
}
|
|
|
|
allTabIds = newTabIds;
|
|
|
|
|
|
|
|
// Mark as "void" all logger entries which are linked to now invalid
|
|
|
|
// tab ids.
|
|
|
|
// When an entry is voided without being removed, we re-create a new entry
|
|
|
|
// in order to ensure the entry has a new identity. A new identify ensures
|
|
|
|
// that identity-based associations elsewhere are automatically
|
|
|
|
// invalidated.
|
|
|
|
if ( toVoid.size !== 0 ) {
|
|
|
|
const autoDeleteVoidedRows = selectedTabValue === '_';
|
|
|
|
let rowVoided = false;
|
|
|
|
for ( let i = 0, n = loggerEntries.length; i < n; i++ ) {
|
|
|
|
const entry = loggerEntries[i];
|
|
|
|
if ( toVoid.has(entry.tabId) === false ) { continue; }
|
|
|
|
if ( entry.voided ) { continue; }
|
|
|
|
rowVoided = entry.voided = true;
|
|
|
|
if ( autoDeleteVoidedRows ) {
|
|
|
|
entry.dead = true;
|
|
|
|
}
|
|
|
|
loggerEntries[i] = new LogEntry(entry);
|
2015-05-16 16:15:02 +02:00
|
|
|
}
|
2019-01-12 22:36:20 +01:00
|
|
|
if ( rowVoided ) {
|
|
|
|
rowFilterer.filterAll();
|
2015-05-16 16:15:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
// Remove popup if it is currently bound to a removed tab.
|
|
|
|
if ( toVoid.has(popupManager.tabId) ) {
|
|
|
|
popupManager.toggleOff();
|
|
|
|
}
|
|
|
|
|
2018-12-13 18:30:54 +01:00
|
|
|
const tabIds = Array.from(newTabIds.keys()).sort(function(a, b) {
|
2018-02-26 19:59:16 +01:00
|
|
|
return newTabIds.get(a).localeCompare(newTabIds.get(b));
|
2015-05-16 16:15:02 +02:00
|
|
|
});
|
2018-12-13 18:30:54 +01:00
|
|
|
let j = 3;
|
|
|
|
for ( let i = 0; i < tabIds.length; i++ ) {
|
|
|
|
const tabId = tabIds[i];
|
2019-01-12 22:36:20 +01:00
|
|
|
if ( tabId <= 0 ) { continue; }
|
|
|
|
if ( j === select.options.length ) {
|
|
|
|
select.appendChild(document.createElement('option'));
|
2015-05-16 16:15:02 +02:00
|
|
|
}
|
2019-01-12 22:36:20 +01:00
|
|
|
const option = select.options[j];
|
2015-10-24 15:24:27 +02:00
|
|
|
// Truncate too long labels.
|
2018-02-26 19:59:16 +01:00
|
|
|
option.textContent = newTabIds.get(tabId).slice(0, 80);
|
2019-01-12 22:36:20 +01:00
|
|
|
option.setAttribute('value', tabId);
|
|
|
|
if ( option.value === selectedTabValue ) {
|
2015-07-02 01:50:43 +02:00
|
|
|
select.selectedIndex = j;
|
2015-05-16 16:15:02 +02:00
|
|
|
option.setAttribute('selected', '');
|
|
|
|
} else {
|
|
|
|
option.removeAttribute('selected');
|
|
|
|
}
|
2015-07-02 01:50:43 +02:00
|
|
|
j += 1;
|
2015-05-16 16:15:02 +02:00
|
|
|
}
|
|
|
|
while ( j < select.options.length ) {
|
|
|
|
select.removeChild(select.options[j]);
|
|
|
|
}
|
2019-01-12 22:36:20 +01:00
|
|
|
if ( select.value !== selectedTabValue ) {
|
2015-05-16 16:15:02 +02:00
|
|
|
select.selectedIndex = 0;
|
|
|
|
select.value = '';
|
|
|
|
select.options[0].setAttribute('selected', '');
|
|
|
|
pageSelectorChanged();
|
|
|
|
}
|
|
|
|
|
2018-05-27 14:31:17 +02:00
|
|
|
updateCurrentTabTitle();
|
2015-05-09 00:28:01 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2018-12-13 18:30:54 +01:00
|
|
|
const onLogBufferRead = function(response) {
|
2018-12-23 21:35:32 +01:00
|
|
|
if ( !response || response.unavailable ) { return; }
|
2018-01-08 20:29:39 +01:00
|
|
|
|
2018-12-17 19:54:17 +01:00
|
|
|
// Disable tooltips?
|
|
|
|
if (
|
|
|
|
popupLoggerTooltips === undefined &&
|
|
|
|
response.tooltips !== undefined
|
|
|
|
) {
|
|
|
|
popupLoggerTooltips = response.tooltips;
|
|
|
|
if ( popupLoggerTooltips === false ) {
|
|
|
|
uDom('[data-i18n-title]').attr('title', '');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-09 14:08:17 +01:00
|
|
|
// Tab id of currently active tab
|
2018-05-27 14:31:17 +02:00
|
|
|
let activeTabIdChanged = false;
|
2018-01-09 14:08:17 +01:00
|
|
|
if ( response.activeTabId ) {
|
2018-05-27 14:31:17 +02:00
|
|
|
activeTabIdChanged = response.activeTabId !== activeTabId;
|
2018-01-09 14:08:17 +01:00
|
|
|
activeTabId = response.activeTabId;
|
|
|
|
}
|
|
|
|
|
2018-05-27 14:31:17 +02:00
|
|
|
if ( Array.isArray(response.tabIds) ) {
|
|
|
|
response.tabIds = new Map(response.tabIds);
|
|
|
|
}
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
// List of tab ids has changed
|
2018-05-27 14:31:17 +02:00
|
|
|
if ( response.tabIds !== undefined ) {
|
2019-01-12 22:36:20 +01:00
|
|
|
synchronizeTabIds(response.tabIds);
|
2015-05-18 14:12:35 +02:00
|
|
|
allTabIdsToken = response.tabIdsToken;
|
|
|
|
}
|
|
|
|
|
2019-09-24 23:05:03 +02:00
|
|
|
filterAuthorMode = response.filterAuthorMode === true;
|
|
|
|
|
2018-05-27 14:31:17 +02:00
|
|
|
if ( activeTabIdChanged ) {
|
|
|
|
pageSelectorFromURLHash();
|
|
|
|
}
|
2015-08-01 17:30:54 +02:00
|
|
|
|
2019-05-24 17:18:39 +02:00
|
|
|
processLoggerEntries(response);
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
// Synchronize DOM with sent logger data
|
|
|
|
document.body.classList.toggle(
|
|
|
|
'colorBlind',
|
|
|
|
response.colorBlind === true
|
|
|
|
);
|
|
|
|
uDom.nodeFromId('clean').classList.toggle(
|
2015-05-09 00:28:01 +02:00
|
|
|
'disabled',
|
2019-01-12 22:36:20 +01:00
|
|
|
filteredLoggerEntryVoidedCount === 0
|
|
|
|
);
|
|
|
|
uDom.nodeFromId('clear').classList.toggle(
|
|
|
|
'disabled',
|
|
|
|
filteredLoggerEntries.length === 0
|
2015-05-09 00:28:01 +02:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-05-24 17:18:39 +02:00
|
|
|
const readLogBuffer = (( ) => {
|
2018-12-23 21:35:32 +01:00
|
|
|
let timer;
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2019-09-17 21:15:01 +02:00
|
|
|
const readLogBufferNow = async function() {
|
2018-12-23 21:35:32 +01:00
|
|
|
if ( logger.ownerId === undefined ) { return; }
|
|
|
|
|
|
|
|
const msg = {
|
|
|
|
what: 'readAll',
|
|
|
|
ownerId: logger.ownerId,
|
|
|
|
tabIdsToken: allTabIdsToken,
|
|
|
|
};
|
|
|
|
|
|
|
|
// This is to detect changes in the position or size of the logger
|
|
|
|
// popup window (if in use).
|
|
|
|
if (
|
|
|
|
popupLoggerBox instanceof Object &&
|
|
|
|
(
|
|
|
|
self.screenX !== popupLoggerBox.x ||
|
|
|
|
self.screenY !== popupLoggerBox.y ||
|
|
|
|
self.outerWidth !== popupLoggerBox.w ||
|
|
|
|
self.outerHeight !== popupLoggerBox.h
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
popupLoggerBox.x = self.screenX;
|
|
|
|
popupLoggerBox.y = self.screenY;
|
|
|
|
popupLoggerBox.w = self.outerWidth;
|
|
|
|
popupLoggerBox.h = self.outerHeight;
|
|
|
|
msg.popupLoggerBoxChanged = true;
|
|
|
|
}
|
|
|
|
|
2019-09-17 21:15:01 +02:00
|
|
|
const response = await vAPI.messaging.send('loggerUI', msg);
|
|
|
|
|
|
|
|
timer = undefined;
|
|
|
|
onLogBufferRead(response);
|
|
|
|
readLogBufferLater();
|
2018-12-14 17:01:21 +01:00
|
|
|
};
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2018-12-23 21:35:32 +01:00
|
|
|
const readLogBufferLater = function() {
|
|
|
|
if ( timer !== undefined ) { return; }
|
|
|
|
if ( logger.ownerId === undefined ) { return; }
|
|
|
|
timer = vAPI.setTimeout(readLogBufferNow, 1200);
|
|
|
|
};
|
|
|
|
|
|
|
|
readLogBufferNow();
|
|
|
|
|
|
|
|
return readLogBufferLater;
|
|
|
|
})();
|
2018-01-08 20:29:39 +01:00
|
|
|
|
2015-05-09 00:28:01 +02:00
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const pageSelectorChanged = function() {
|
|
|
|
const select = uDom.nodeFromId('pageSelector');
|
2018-01-09 14:08:17 +01:00
|
|
|
window.location.replace('#' + select.value);
|
2015-08-01 17:30:54 +02:00
|
|
|
pageSelectorFromURLHash();
|
2015-05-16 16:15:02 +02:00
|
|
|
};
|
|
|
|
|
2019-05-24 17:18:39 +02:00
|
|
|
const pageSelectorFromURLHash = (( ) => {
|
2019-01-12 22:36:20 +01:00
|
|
|
let lastHash;
|
|
|
|
let lastSelectedTabId;
|
2018-01-09 14:08:17 +01:00
|
|
|
|
|
|
|
return function() {
|
2019-01-12 22:36:20 +01:00
|
|
|
let hash = window.location.hash.slice(1);
|
|
|
|
let match = /^([^+]+)\+(.+)$/.exec(hash);
|
2018-05-27 14:31:17 +02:00
|
|
|
if ( match !== null ) {
|
2019-01-12 22:36:20 +01:00
|
|
|
hash = match[1];
|
|
|
|
activeTabId = parseInt(match[2], 10) || 0;
|
|
|
|
window.location.hash = '#' + hash;
|
2018-05-27 14:31:17 +02:00
|
|
|
}
|
2015-08-01 17:30:54 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
if ( hash !== lastHash ) {
|
|
|
|
const select = uDom.nodeFromId('pageSelector');
|
|
|
|
let option = select.querySelector(
|
|
|
|
'option[value="' + hash + '"]'
|
|
|
|
);
|
|
|
|
if ( option === null ) {
|
|
|
|
hash = '0';
|
|
|
|
option = select.options[0];
|
|
|
|
}
|
|
|
|
select.selectedIndex = option.index;
|
|
|
|
select.value = option.value;
|
|
|
|
lastHash = hash;
|
2015-08-01 17:30:54 +02:00
|
|
|
}
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
selectedTabId = hash === '_'
|
|
|
|
? activeTabId
|
|
|
|
: parseInt(hash, 10) || 0;
|
2015-08-01 17:30:54 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
if ( lastSelectedTabId === selectedTabId ) { return; }
|
|
|
|
|
|
|
|
rowFilterer.filterAll();
|
|
|
|
document.dispatchEvent(new Event('tabIdChanged'));
|
|
|
|
updateCurrentTabTitle();
|
|
|
|
uDom('.needdom').toggleClass('disabled', selectedTabId <= 0);
|
|
|
|
uDom('.needscope').toggleClass('disabled', selectedTabId <= 0);
|
|
|
|
lastSelectedTabId = selectedTabId;
|
2015-08-01 17:30:54 +02:00
|
|
|
};
|
|
|
|
})();
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const reloadTab = function(ev) {
|
|
|
|
const tabId = tabIdFromPageSelector();
|
2018-12-13 18:30:54 +01:00
|
|
|
if ( tabId <= 0 ) { return; }
|
2018-01-10 17:50:08 +01:00
|
|
|
messaging.send('loggerUI', {
|
|
|
|
what: 'reloadTab',
|
|
|
|
tabId: tabId,
|
2019-09-17 21:15:01 +02:00
|
|
|
bypassCache: ev && (ev.ctrlKey || ev.metaKey || ev.shiftKey),
|
2018-01-10 17:50:08 +01:00
|
|
|
});
|
2015-05-16 16:15:02 +02:00
|
|
|
};
|
|
|
|
|
2015-05-21 20:15:17 +02:00
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-09-24 23:05:03 +02:00
|
|
|
(( ) => {
|
2019-01-12 22:36:20 +01:00
|
|
|
const reRFC3986 = /^([^:\/?#]+:)?(\/\/[^\/?#]*)?([^?#]*)(\?[^#]*)?(#.*)?/;
|
2019-05-19 23:00:49 +02:00
|
|
|
const reSchemeOnly = /^[\w-]+:$/;
|
2019-01-12 22:36:20 +01:00
|
|
|
const staticFilterTypes = {
|
2019-09-22 15:11:55 +02:00
|
|
|
'beacon': 'ping',
|
2019-01-12 22:36:20 +01:00
|
|
|
'doc': 'document',
|
|
|
|
'css': 'stylesheet',
|
|
|
|
'frame': 'subdocument',
|
|
|
|
'object_subrequest': 'object',
|
|
|
|
};
|
|
|
|
const createdStaticFilters = {};
|
|
|
|
|
|
|
|
let dialog = null;
|
|
|
|
let targetRow = null;
|
|
|
|
let targetType;
|
|
|
|
let targetURLs = [];
|
|
|
|
let targetFrameHostname;
|
|
|
|
let targetPageHostname;
|
|
|
|
let targetTabId;
|
|
|
|
let targetDomain;
|
|
|
|
let targetPageDomain;
|
|
|
|
let targetFrameDomain;
|
|
|
|
|
|
|
|
const uglyTypeFromSelector = function(pane) {
|
|
|
|
const prettyType = selectValue('select.type.' + pane);
|
2015-06-18 23:23:52 +02:00
|
|
|
if ( pane === 'static' ) {
|
|
|
|
return staticFilterTypes[prettyType] || prettyType;
|
|
|
|
}
|
2015-05-21 20:15:17 +02:00
|
|
|
return uglyRequestTypes[prettyType] || prettyType;
|
|
|
|
};
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const selectNode = function(selector) {
|
2015-06-07 00:31:38 +02:00
|
|
|
return dialog.querySelector(selector);
|
|
|
|
};
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const selectValue = function(selector) {
|
2015-06-07 00:31:38 +02:00
|
|
|
return selectNode(selector).value || '';
|
|
|
|
};
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const staticFilterNode = function() {
|
|
|
|
return dialog.querySelector('div.panes > div.static textarea');
|
2015-06-07 00:31:38 +02:00
|
|
|
};
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const onColorsReady = function(response) {
|
2015-05-21 20:15:17 +02:00
|
|
|
document.body.classList.toggle('dirty', response.dirty);
|
2019-01-12 22:36:20 +01:00
|
|
|
for ( const url in response.colors ) {
|
|
|
|
if ( response.colors.hasOwnProperty(url) === false ) { continue; }
|
|
|
|
const colorEntry = response.colors[url];
|
|
|
|
const node = dialog.querySelector('.dynamic .entry .action[data-url="' + url + '"]');
|
|
|
|
if ( node === null ) { continue; }
|
2015-05-21 20:15:17 +02:00
|
|
|
node.classList.toggle('allow', colorEntry.r === 2);
|
|
|
|
node.classList.toggle('noop', colorEntry.r === 3);
|
|
|
|
node.classList.toggle('block', colorEntry.r === 1);
|
|
|
|
node.classList.toggle('own', colorEntry.own);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-09-17 21:15:01 +02:00
|
|
|
const colorize = async function() {
|
|
|
|
const response = await messaging.send('loggerUI', {
|
|
|
|
what: 'getURLFilteringData',
|
|
|
|
context: selectValue('select.dynamic.origin'),
|
|
|
|
urls: targetURLs,
|
|
|
|
type: uglyTypeFromSelector('dynamic'),
|
|
|
|
});
|
|
|
|
onColorsReady(response);
|
2015-05-21 20:15:17 +02:00
|
|
|
};
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const parseStaticInputs = function() {
|
|
|
|
const options = [];
|
|
|
|
const block = selectValue('select.static.action') === '';
|
|
|
|
let filter = '';
|
2015-06-07 00:45:30 +02:00
|
|
|
if ( !block ) {
|
2015-06-07 00:31:38 +02:00
|
|
|
filter = '@@';
|
|
|
|
}
|
2019-01-12 22:36:20 +01:00
|
|
|
let value = selectValue('select.static.url');
|
2015-06-07 00:31:38 +02:00
|
|
|
if ( value !== '' ) {
|
2019-05-19 23:00:49 +02:00
|
|
|
if ( reSchemeOnly.test(value) ) {
|
|
|
|
value = `|${value}`;
|
|
|
|
} else {
|
|
|
|
if ( value.endsWith('/') ) {
|
|
|
|
value += '*';
|
|
|
|
} else if ( /[/?]/.test(value) === false ) {
|
|
|
|
value += '^';
|
|
|
|
}
|
|
|
|
value = `||${value}`;
|
2017-07-03 16:20:47 +02:00
|
|
|
}
|
2015-06-07 00:31:38 +02:00
|
|
|
}
|
2017-07-03 16:20:47 +02:00
|
|
|
filter += value;
|
2015-06-07 00:31:38 +02:00
|
|
|
value = selectValue('select.static.type');
|
|
|
|
if ( value !== '' ) {
|
|
|
|
options.push(uglyTypeFromSelector('static'));
|
|
|
|
}
|
|
|
|
value = selectValue('select.static.origin');
|
|
|
|
if ( value !== '' ) {
|
|
|
|
if ( value === targetDomain ) {
|
2019-09-22 15:11:55 +02:00
|
|
|
options.push('1p');
|
2015-06-07 00:31:38 +02:00
|
|
|
} else {
|
|
|
|
options.push('domain=' + value);
|
|
|
|
}
|
|
|
|
}
|
2015-06-07 00:45:30 +02:00
|
|
|
if ( block && selectValue('select.static.importance') !== '' ) {
|
|
|
|
options.push('important');
|
|
|
|
}
|
2015-06-07 00:31:38 +02:00
|
|
|
if ( options.length ) {
|
|
|
|
filter += '$' + options.join(',');
|
|
|
|
}
|
|
|
|
staticFilterNode().value = filter;
|
|
|
|
updateWidgets();
|
|
|
|
};
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const updateWidgets = function() {
|
|
|
|
const value = staticFilterNode().value;
|
2015-06-07 00:31:38 +02:00
|
|
|
dialog.querySelector('#createStaticFilter').classList.toggle(
|
|
|
|
'disabled',
|
|
|
|
createdStaticFilters.hasOwnProperty(value) || value === ''
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2019-09-24 23:05:03 +02:00
|
|
|
const onClick = async function(ev) {
|
2019-01-12 22:36:20 +01:00
|
|
|
const target = ev.target;
|
|
|
|
const tcl = target.classList;
|
2015-06-07 00:31:38 +02:00
|
|
|
|
|
|
|
// Select a mode
|
|
|
|
if ( tcl.contains('header') ) {
|
2019-01-12 22:36:20 +01:00
|
|
|
ev.stopPropagation();
|
2019-09-24 23:05:03 +02:00
|
|
|
dialog.setAttribute('data-pane', target.getAttribute('data-pane') );
|
2015-06-07 00:31:38 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-09-24 23:05:03 +02:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2015-06-07 00:31:38 +02:00
|
|
|
// Create static filter
|
|
|
|
if ( target.id === 'createStaticFilter' ) {
|
2019-09-24 23:05:03 +02:00
|
|
|
ev.stopPropagation();
|
2019-01-12 22:36:20 +01:00
|
|
|
const value = staticFilterNode().value;
|
2015-06-07 00:31:38 +02:00
|
|
|
// Avoid duplicates
|
2019-09-24 23:05:03 +02:00
|
|
|
if ( createdStaticFilters.hasOwnProperty(value) ) { return; }
|
2015-06-07 00:31:38 +02:00
|
|
|
createdStaticFilters[value] = true;
|
2020-10-07 17:52:38 +02:00
|
|
|
// https://github.com/uBlockOrigin/uBlock-issues/issues/1281#issuecomment-704217175
|
|
|
|
// TODO:
|
|
|
|
// Figure a way to use the actual document URL. Currently using
|
|
|
|
// a synthetic URL derived from the document hostname.
|
2015-06-07 00:31:38 +02:00
|
|
|
if ( value !== '' ) {
|
2019-09-17 21:15:01 +02:00
|
|
|
messaging.send('loggerUI', {
|
|
|
|
what: 'createUserFilter',
|
|
|
|
autoComment: true,
|
|
|
|
filters: value,
|
2020-10-07 17:52:38 +02:00
|
|
|
docURL: `https://${targetFrameHostname}/`,
|
2019-09-17 21:15:01 +02:00
|
|
|
});
|
2015-06-07 00:31:38 +02:00
|
|
|
}
|
|
|
|
updateWidgets();
|
|
|
|
return;
|
|
|
|
}
|
2015-05-26 15:52:09 +02:00
|
|
|
|
2015-05-21 20:15:17 +02:00
|
|
|
// Save url filtering rule(s)
|
2015-06-07 00:31:38 +02:00
|
|
|
if ( target.id === 'saveRules' ) {
|
2019-09-24 23:05:03 +02:00
|
|
|
ev.stopPropagation();
|
|
|
|
await messaging.send('loggerUI', {
|
2019-09-17 21:15:01 +02:00
|
|
|
what: 'saveURLFilteringRules',
|
|
|
|
context: selectValue('select.dynamic.origin'),
|
|
|
|
urls: targetURLs,
|
|
|
|
type: uglyTypeFromSelector('dynamic'),
|
|
|
|
});
|
2019-09-24 23:05:03 +02:00
|
|
|
colorize();
|
2015-05-21 20:15:17 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const persist = !!ev.ctrlKey || !!ev.metaKey;
|
2015-05-22 14:05:55 +02:00
|
|
|
|
2015-05-21 20:15:17 +02:00
|
|
|
// Remove url filtering rule
|
2015-05-26 15:52:09 +02:00
|
|
|
if ( tcl.contains('action') ) {
|
2019-09-24 23:05:03 +02:00
|
|
|
ev.stopPropagation();
|
|
|
|
await messaging.send('loggerUI', {
|
2019-09-17 21:15:01 +02:00
|
|
|
what: 'setURLFilteringRule',
|
|
|
|
context: selectValue('select.dynamic.origin'),
|
|
|
|
url: target.getAttribute('data-url'),
|
|
|
|
type: uglyTypeFromSelector('dynamic'),
|
|
|
|
action: 0,
|
|
|
|
persist: persist,
|
|
|
|
});
|
2019-09-24 23:05:03 +02:00
|
|
|
colorize();
|
2015-05-21 20:15:17 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// add "allow" url filtering rule
|
2015-05-26 15:52:09 +02:00
|
|
|
if ( tcl.contains('allow') ) {
|
2019-09-24 23:05:03 +02:00
|
|
|
ev.stopPropagation();
|
|
|
|
await messaging.send('loggerUI', {
|
2019-09-17 21:15:01 +02:00
|
|
|
what: 'setURLFilteringRule',
|
|
|
|
context: selectValue('select.dynamic.origin'),
|
|
|
|
url: target.parentNode.getAttribute('data-url'),
|
|
|
|
type: uglyTypeFromSelector('dynamic'),
|
|
|
|
action: 2,
|
|
|
|
persist: persist,
|
|
|
|
});
|
2019-09-24 23:05:03 +02:00
|
|
|
colorize();
|
2015-05-21 20:15:17 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// add "block" url filtering rule
|
2015-05-26 15:52:09 +02:00
|
|
|
if ( tcl.contains('noop') ) {
|
2019-09-24 23:05:03 +02:00
|
|
|
ev.stopPropagation();
|
|
|
|
await messaging.send('loggerUI', {
|
2019-09-17 21:15:01 +02:00
|
|
|
what: 'setURLFilteringRule',
|
|
|
|
context: selectValue('select.dynamic.origin'),
|
|
|
|
url: target.parentNode.getAttribute('data-url'),
|
|
|
|
type: uglyTypeFromSelector('dynamic'),
|
|
|
|
action: 3,
|
|
|
|
persist: persist,
|
|
|
|
});
|
2019-09-24 23:05:03 +02:00
|
|
|
colorize();
|
2015-05-21 20:15:17 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// add "block" url filtering rule
|
2015-05-26 15:52:09 +02:00
|
|
|
if ( tcl.contains('block') ) {
|
2019-09-24 23:05:03 +02:00
|
|
|
ev.stopPropagation();
|
|
|
|
await messaging.send('loggerUI', {
|
2019-09-17 21:15:01 +02:00
|
|
|
what: 'setURLFilteringRule',
|
|
|
|
context: selectValue('select.dynamic.origin'),
|
|
|
|
url: target.parentNode.getAttribute('data-url'),
|
|
|
|
type: uglyTypeFromSelector('dynamic'),
|
|
|
|
action: 1,
|
|
|
|
persist: persist,
|
|
|
|
});
|
2019-09-24 23:05:03 +02:00
|
|
|
colorize();
|
2015-05-21 20:15:17 +02:00
|
|
|
return;
|
|
|
|
}
|
2015-05-25 00:50:09 +02:00
|
|
|
|
|
|
|
// Force a reload of the tab
|
2015-05-26 15:52:09 +02:00
|
|
|
if ( tcl.contains('reload') ) {
|
2019-09-24 23:05:03 +02:00
|
|
|
ev.stopPropagation();
|
2019-09-17 21:15:01 +02:00
|
|
|
messaging.send('loggerUI', {
|
|
|
|
what: 'reloadTab',
|
|
|
|
tabId: targetTabId,
|
|
|
|
});
|
2015-05-25 00:50:09 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Hightlight corresponding element in target web page
|
2015-05-26 15:52:09 +02:00
|
|
|
if ( tcl.contains('picker') ) {
|
2019-09-24 23:05:03 +02:00
|
|
|
ev.stopPropagation();
|
2019-09-17 21:15:01 +02:00
|
|
|
messaging.send('loggerUI', {
|
|
|
|
what: 'launchElementPicker',
|
|
|
|
tabId: targetTabId,
|
|
|
|
targetURL: 'img\t' + targetURLs[0],
|
|
|
|
select: true,
|
|
|
|
});
|
2015-05-25 00:50:09 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const onSelectChange = function(ev) {
|
|
|
|
const tcl = ev.target.classList;
|
2015-06-07 00:31:38 +02:00
|
|
|
|
|
|
|
if ( tcl.contains('dynamic') ) {
|
|
|
|
colorize();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( tcl.contains('static') ) {
|
|
|
|
parseStaticInputs();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const onInputChange = function() {
|
2015-06-07 00:31:38 +02:00
|
|
|
updateWidgets();
|
|
|
|
};
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const createPreview = function(type, url) {
|
|
|
|
const cantPreview =
|
|
|
|
type !== 'image' ||
|
|
|
|
targetRow.classList.contains('networkRealm') === false ||
|
2019-01-13 14:34:17 +01:00
|
|
|
targetRow.getAttribute('data-status') === '1';
|
2019-01-12 22:36:20 +01:00
|
|
|
|
|
|
|
// Whether picker can be used
|
2015-06-07 00:31:38 +02:00
|
|
|
dialog.querySelector('.picker').classList.toggle(
|
|
|
|
'hide',
|
2019-01-12 22:36:20 +01:00
|
|
|
targetTabId < 0 || cantPreview
|
2015-06-07 00:31:38 +02:00
|
|
|
);
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
// Whether the resource can be previewed
|
|
|
|
if ( cantPreview ) { return; }
|
2015-05-25 00:50:09 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const container = dialog.querySelector('.preview');
|
|
|
|
container.querySelector('span').addEventListener(
|
|
|
|
'click',
|
|
|
|
( ) => {
|
|
|
|
const preview = document.createElement('img');
|
|
|
|
preview.setAttribute('src', url);
|
|
|
|
container.replaceChild(preview, container.firstElementChild);
|
|
|
|
},
|
|
|
|
{ once: true }
|
|
|
|
);
|
2015-05-25 00:50:09 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
container.classList.remove('hide');
|
2015-05-21 20:15:17 +02:00
|
|
|
};
|
|
|
|
|
2016-03-28 15:31:53 +02:00
|
|
|
// https://github.com/gorhill/uBlock/issues/1511
|
2019-01-12 22:36:20 +01:00
|
|
|
const shortenLongString = function(url, max) {
|
|
|
|
const urlLen = url.length;
|
2016-03-28 15:31:53 +02:00
|
|
|
if ( urlLen <= max ) {
|
|
|
|
return url;
|
|
|
|
}
|
2019-01-12 22:36:20 +01:00
|
|
|
const n = urlLen - max - 1;
|
|
|
|
const i = (urlLen - n) / 2 | 0;
|
|
|
|
return url.slice(0, i) + '…' + url.slice(i + n);
|
|
|
|
};
|
|
|
|
|
|
|
|
// Build list of candidate URLs
|
|
|
|
const createTargetURLs = function(url) {
|
|
|
|
const matches = reRFC3986.exec(url);
|
2019-05-19 23:00:49 +02:00
|
|
|
if ( matches === null ) { return []; }
|
|
|
|
if ( typeof matches[2] !== 'string' || matches[2].length === 0 ) {
|
|
|
|
return [ matches[1] ];
|
2019-01-12 22:36:20 +01:00
|
|
|
}
|
|
|
|
// Shortest URL for a valid URL filtering rule
|
2019-05-19 23:00:49 +02:00
|
|
|
const urls = [];
|
2019-01-12 22:36:20 +01:00
|
|
|
const rootURL = matches[1] + matches[2];
|
|
|
|
urls.unshift(rootURL);
|
|
|
|
const path = matches[3] || '';
|
|
|
|
let pos = path.charAt(0) === '/' ? 1 : 0;
|
|
|
|
while ( pos < path.length ) {
|
|
|
|
pos = path.indexOf('/', pos);
|
|
|
|
if ( pos === -1 ) {
|
|
|
|
pos = path.length;
|
|
|
|
} else {
|
|
|
|
pos += 1;
|
|
|
|
}
|
|
|
|
urls.unshift(rootURL + path.slice(0, pos));
|
|
|
|
}
|
|
|
|
const query = matches[4] || '';
|
|
|
|
if ( query !== '' ) {
|
|
|
|
urls.unshift(rootURL + path + query);
|
|
|
|
}
|
|
|
|
return urls;
|
|
|
|
};
|
|
|
|
|
2019-09-24 23:05:03 +02:00
|
|
|
const filterFromTargetRow = function() {
|
|
|
|
return targetRow.children[1].textContent;
|
|
|
|
};
|
|
|
|
|
2019-11-23 18:46:52 +01:00
|
|
|
const aliasURLFromID = function(id) {
|
|
|
|
if ( id === '' ) { return ''; }
|
|
|
|
for ( const entry of loggerEntries ) {
|
|
|
|
if ( entry.id !== id || entry.aliased ) { continue; }
|
|
|
|
const fields = entry.textContent.split('\t');
|
|
|
|
return fields[6] || '';
|
|
|
|
}
|
|
|
|
return '';
|
|
|
|
};
|
|
|
|
|
2019-09-24 23:05:03 +02:00
|
|
|
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();
|
2019-09-25 12:14:43 +02:00
|
|
|
const pos = match.index + match[0].length;
|
|
|
|
fragment.appendChild(document.createTextNode(filter.slice(0, pos)));
|
|
|
|
const selector = filter.slice(pos);
|
2019-09-24 23:05:03 +02:00
|
|
|
const span = document.createElement('span');
|
|
|
|
span.className = 'filter';
|
|
|
|
span.textContent = selector;
|
|
|
|
fragment.appendChild(span);
|
2019-09-25 17:21:34 +02:00
|
|
|
const isTemporaryException = await messaging.send('loggerUI', {
|
|
|
|
what: 'hasTemporaryException',
|
|
|
|
filter,
|
|
|
|
});
|
|
|
|
receiver.classList.toggle('exceptored', isTemporaryException);
|
2019-09-24 23:05:03 +02:00
|
|
|
if ( match[0] === '##' || isTemporaryException ) {
|
|
|
|
receiver.children[2].style.visibility = '';
|
|
|
|
}
|
|
|
|
receiver.children[1].textContent = '';
|
|
|
|
receiver.children[1].appendChild(fragment);
|
|
|
|
};
|
|
|
|
|
2019-09-17 21:15:01 +02:00
|
|
|
const fillSummaryPaneFilterList = async function(rows) {
|
2019-01-14 20:57:31 +01:00
|
|
|
const rawFilter = targetRow.children[1].textContent;
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const nodeFromFilter = function(filter, lists) {
|
|
|
|
const fragment = document.createDocumentFragment();
|
|
|
|
const template = document.querySelector(
|
|
|
|
'#filterFinderListEntry > span'
|
|
|
|
);
|
|
|
|
for ( const list of lists ) {
|
|
|
|
const span = template.cloneNode(true);
|
|
|
|
let a = span.querySelector('a:nth-of-type(1)');
|
|
|
|
a.href += encodeURIComponent(list.assetKey);
|
|
|
|
a.textContent = list.title;
|
2019-01-14 20:57:31 +01:00
|
|
|
a = span.querySelector('a:nth-of-type(2)');
|
2019-01-12 22:36:20 +01:00
|
|
|
if ( list.supportURL ) {
|
|
|
|
a.setAttribute('href', list.supportURL);
|
2019-01-14 20:57:31 +01:00
|
|
|
} else {
|
|
|
|
a.style.display = 'none';
|
2019-01-12 22:36:20 +01:00
|
|
|
}
|
|
|
|
if ( fragment.childElementCount !== 0 ) {
|
|
|
|
fragment.appendChild(document.createTextNode('\n'));
|
|
|
|
}
|
|
|
|
fragment.appendChild(span);
|
|
|
|
}
|
|
|
|
return fragment;
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleResponse = function(response) {
|
|
|
|
if ( response instanceof Object === false ) {
|
|
|
|
response = {};
|
|
|
|
}
|
2019-01-14 20:57:31 +01:00
|
|
|
let bestMatchFilter = '';
|
2019-01-12 22:36:20 +01:00
|
|
|
for ( const filter in response ) {
|
2019-01-14 20:57:31 +01:00
|
|
|
if ( filter.length > bestMatchFilter.length ) {
|
|
|
|
bestMatchFilter = filter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (
|
|
|
|
bestMatchFilter !== '' &&
|
|
|
|
Array.isArray(response[bestMatchFilter])
|
|
|
|
) {
|
2019-09-24 23:05:03 +02:00
|
|
|
toSummaryPaneFilterNode(rows[0], bestMatchFilter);
|
2019-01-14 20:57:31 +01:00
|
|
|
rows[1].children[1].appendChild(nodeFromFilter(
|
|
|
|
bestMatchFilter,
|
|
|
|
response[bestMatchFilter]
|
|
|
|
));
|
2019-01-12 22:36:20 +01:00
|
|
|
}
|
|
|
|
// https://github.com/gorhill/uBlock/issues/2179
|
2019-01-14 20:57:31 +01:00
|
|
|
if ( rows[1].children[1].childElementCount === 0 ) {
|
2019-01-12 22:36:20 +01:00
|
|
|
vAPI.i18n.safeTemplateToDOM(
|
|
|
|
'loggerStaticFilteringFinderSentence2',
|
|
|
|
{ filter: rawFilter },
|
2019-01-14 20:57:31 +01:00
|
|
|
rows[1].children[1]
|
2019-01-12 22:36:20 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if ( targetRow.classList.contains('networkRealm') ) {
|
2019-09-17 21:15:01 +02:00
|
|
|
const response = await messaging.send('loggerUI', {
|
|
|
|
what: 'listsFromNetFilter',
|
|
|
|
rawFilter: rawFilter,
|
|
|
|
});
|
|
|
|
handleResponse(response);
|
2021-03-13 14:53:34 +01:00
|
|
|
} else if ( targetRow.classList.contains('extendedRealm') ) {
|
2019-09-17 21:15:01 +02:00
|
|
|
const response = await messaging.send('loggerUI', {
|
|
|
|
what: 'listsFromCosmeticFilter',
|
|
|
|
url: targetRow.children[6].textContent,
|
|
|
|
rawFilter: rawFilter,
|
|
|
|
});
|
|
|
|
handleResponse(response);
|
2019-01-12 22:36:20 +01:00
|
|
|
}
|
2021-03-13 14:53:34 +01:00
|
|
|
};
|
2019-01-12 22:36:20 +01:00
|
|
|
|
|
|
|
const fillSummaryPane = function() {
|
|
|
|
const rows = dialog.querySelectorAll('.pane.details > div');
|
|
|
|
const tr = targetRow;
|
|
|
|
const trcl = tr.classList;
|
|
|
|
const trch = tr.children;
|
|
|
|
let text;
|
|
|
|
// Filter and context
|
2019-09-24 23:05:03 +02:00
|
|
|
text = filterFromTargetRow();
|
2019-01-12 22:36:20 +01:00
|
|
|
if (
|
|
|
|
(text !== '') &&
|
2021-03-13 14:53:34 +01:00
|
|
|
(trcl.contains('extendedRealm') || trcl.contains('networkRealm'))
|
2019-01-12 22:36:20 +01:00
|
|
|
) {
|
2019-09-24 23:05:03 +02:00
|
|
|
toSummaryPaneFilterNode(rows[0], text);
|
2019-01-12 22:36:20 +01:00
|
|
|
} else {
|
|
|
|
rows[0].style.display = 'none';
|
|
|
|
}
|
|
|
|
// Rule
|
|
|
|
if (
|
|
|
|
(text !== '') &&
|
2019-01-14 22:52:13 +01:00
|
|
|
(
|
|
|
|
trcl.contains('dynamicHost') ||
|
|
|
|
trcl.contains('dynamicUrl') ||
|
2021-03-13 14:53:34 +01:00
|
|
|
trcl.contains('switchRealm')
|
2019-01-14 22:52:13 +01:00
|
|
|
)
|
2019-01-12 22:36:20 +01:00
|
|
|
) {
|
|
|
|
rows[2].children[1].textContent = text;
|
|
|
|
} else {
|
|
|
|
rows[2].style.display = 'none';
|
|
|
|
}
|
|
|
|
// Filter list
|
|
|
|
if ( trcl.contains('canLookup') ) {
|
2019-01-14 20:57:31 +01:00
|
|
|
fillSummaryPaneFilterList(rows);
|
2019-01-12 22:36:20 +01:00
|
|
|
} else {
|
|
|
|
rows[1].style.display = 'none';
|
|
|
|
}
|
|
|
|
// Root and immediate contexts
|
|
|
|
const tabhn = tr.getAttribute('data-tabhn') || '';
|
|
|
|
const dochn = tr.getAttribute('data-dochn') || '';
|
|
|
|
if ( tabhn !== '' && tabhn !== dochn ) {
|
|
|
|
rows[3].children[1].textContent = tabhn;
|
|
|
|
} else {
|
|
|
|
rows[3].style.display = 'none';
|
2015-05-26 15:52:09 +02:00
|
|
|
}
|
2019-01-12 22:36:20 +01:00
|
|
|
if ( dochn !== '' ) {
|
|
|
|
rows[4].children[1].textContent = dochn;
|
|
|
|
} else {
|
|
|
|
rows[4].style.display = 'none';
|
2015-06-07 00:31:38 +02:00
|
|
|
}
|
2019-01-12 22:36:20 +01:00
|
|
|
// Partyness
|
|
|
|
text = tr.getAttribute('data-parties') || '';
|
|
|
|
if ( text !== '' ) {
|
2019-01-14 20:57:31 +01:00
|
|
|
rows[5].children[1].textContent = `(${trch[4].textContent})\u2002${text}`;
|
2019-01-12 22:36:20 +01:00
|
|
|
} else {
|
|
|
|
rows[5].style.display = 'none';
|
|
|
|
}
|
|
|
|
// Type
|
|
|
|
text = trch[5].textContent;
|
|
|
|
if ( text !== '' ) {
|
|
|
|
rows[6].children[1].textContent = text;
|
|
|
|
} else {
|
|
|
|
rows[6].style.display = 'none';
|
|
|
|
}
|
|
|
|
// URL
|
2019-11-23 18:46:52 +01:00
|
|
|
const canonicalURL = trch[6].textContent;
|
|
|
|
if ( canonicalURL !== '' ) {
|
2019-01-13 14:34:17 +01:00
|
|
|
const attr = tr.getAttribute('data-status') || '';
|
|
|
|
if ( attr !== '' ) {
|
|
|
|
rows[7].setAttribute('data-status', attr);
|
2020-11-20 13:14:02 +01:00
|
|
|
if ( tr.hasAttribute('data-modifier') ) {
|
|
|
|
rows[7].setAttribute('data-modifier', '');
|
|
|
|
}
|
2019-01-13 14:34:17 +01:00
|
|
|
}
|
2019-01-12 22:36:20 +01:00
|
|
|
rows[7].children[1].appendChild(trch[6].cloneNode(true));
|
|
|
|
} else {
|
|
|
|
rows[7].style.display = 'none';
|
2015-06-07 00:31:38 +02:00
|
|
|
}
|
2019-11-23 18:46:52 +01:00
|
|
|
// Alias URL
|
|
|
|
text = tr.getAttribute('data-aliasid');
|
|
|
|
const aliasURL = text ? aliasURLFromID(text) : '';
|
|
|
|
if ( aliasURL !== '' ) {
|
|
|
|
rows[8].children[1].textContent =
|
|
|
|
vAPI.hostnameFromURI(aliasURL) + ' \u21d2\n\u2003' +
|
|
|
|
vAPI.hostnameFromURI(canonicalURL);
|
|
|
|
rows[9].children[1].textContent = aliasURL;
|
2019-11-19 18:05:33 +01:00
|
|
|
} else {
|
|
|
|
rows[8].style.display = 'none';
|
2019-11-23 18:46:52 +01:00
|
|
|
rows[9].style.display = 'none';
|
2019-11-19 18:05:33 +01:00
|
|
|
}
|
2015-06-07 00:31:38 +02:00
|
|
|
};
|
2015-05-26 15:52:09 +02:00
|
|
|
|
2015-06-07 00:31:38 +02:00
|
|
|
// Fill dynamic URL filtering pane
|
2019-01-12 22:36:20 +01:00
|
|
|
const fillDynamicPane = function() {
|
2021-03-13 14:53:34 +01:00
|
|
|
if ( targetRow.classList.contains('extendedRealm') ) {
|
|
|
|
return;
|
|
|
|
}
|
2019-01-12 22:36:20 +01:00
|
|
|
|
2019-07-08 16:49:53 +02:00
|
|
|
// https://github.com/uBlockOrigin/uBlock-issues/issues/662#issuecomment-509220702
|
|
|
|
if ( targetType === 'doc' ) { return; }
|
|
|
|
|
2019-05-19 23:00:49 +02:00
|
|
|
// https://github.com/gorhill/uBlock/issues/2469
|
|
|
|
if ( targetURLs.length === 0 || reSchemeOnly.test(targetURLs[0]) ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-06-07 00:31:38 +02:00
|
|
|
// Fill context selector
|
2019-01-12 22:36:20 +01:00
|
|
|
let select = selectNode('select.dynamic.origin');
|
2015-06-07 00:31:38 +02:00
|
|
|
fillOriginSelect(select, targetPageHostname, targetPageDomain);
|
2019-01-12 22:36:20 +01:00
|
|
|
const option = document.createElement('option');
|
2015-06-07 00:31:38 +02:00
|
|
|
option.textContent = '*';
|
|
|
|
option.setAttribute('value', '*');
|
|
|
|
select.appendChild(option);
|
2015-05-21 20:15:17 +02:00
|
|
|
|
2015-06-07 00:31:38 +02:00
|
|
|
// Fill type selector
|
|
|
|
select = selectNode('select.dynamic.type');
|
|
|
|
select.options[0].textContent = targetType;
|
|
|
|
select.options[0].setAttribute('value', targetType);
|
|
|
|
select.selectedIndex = 0;
|
2015-05-21 20:15:17 +02:00
|
|
|
|
2015-06-07 00:31:38 +02:00
|
|
|
// Fill entries
|
2019-01-12 22:36:20 +01:00
|
|
|
const menuEntryTemplate = dialog.querySelector('.dynamic .toolbar .entry');
|
|
|
|
const tbody = dialog.querySelector('.dynamic .entries');
|
2019-05-19 23:00:49 +02:00
|
|
|
for ( const targetURL of targetURLs ) {
|
2019-01-12 22:36:20 +01:00
|
|
|
const menuEntry = menuEntryTemplate.cloneNode(true);
|
2019-05-19 23:00:49 +02:00
|
|
|
menuEntry.children[0].setAttribute('data-url', targetURL);
|
|
|
|
menuEntry.children[1].textContent = shortenLongString(targetURL, 128);
|
2015-06-07 00:31:38 +02:00
|
|
|
tbody.appendChild(menuEntry);
|
2015-05-21 20:15:17 +02:00
|
|
|
}
|
|
|
|
|
2015-06-07 00:31:38 +02:00
|
|
|
colorize();
|
|
|
|
};
|
2015-05-21 20:15:17 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const fillOriginSelect = function(select, hostname, domain) {
|
|
|
|
const template = vAPI.i18n('loggerStaticFilteringSentencePartOrigin');
|
|
|
|
let value = hostname;
|
2015-05-21 20:15:17 +02:00
|
|
|
for (;;) {
|
2019-01-12 22:36:20 +01:00
|
|
|
const option = document.createElement('option');
|
2015-06-07 00:31:38 +02:00
|
|
|
option.setAttribute('value', value);
|
2015-06-07 15:50:38 +02:00
|
|
|
option.textContent = template.replace('{{origin}}', value);
|
2015-06-07 00:31:38 +02:00
|
|
|
select.appendChild(option);
|
2019-01-12 22:36:20 +01:00
|
|
|
if ( value === domain ) { break; }
|
|
|
|
const pos = value.indexOf('.');
|
|
|
|
if ( pos === -1 ) { break; }
|
2015-06-07 00:31:38 +02:00
|
|
|
value = value.slice(pos + 1);
|
2015-05-21 20:15:17 +02:00
|
|
|
}
|
2015-06-07 00:31:38 +02:00
|
|
|
};
|
2015-05-21 20:15:17 +02:00
|
|
|
|
2015-06-07 00:31:38 +02:00
|
|
|
// Fill static filtering pane
|
2019-01-12 22:36:20 +01:00
|
|
|
const fillStaticPane = function() {
|
2021-03-13 14:53:34 +01:00
|
|
|
if ( targetRow.classList.contains('extendedRealm') ) {
|
|
|
|
return;
|
|
|
|
}
|
2019-01-12 22:36:20 +01:00
|
|
|
|
|
|
|
const template = vAPI.i18n('loggerStaticFilteringSentence');
|
|
|
|
const rePlaceholder = /\{\{[^}]+?\}\}/g;
|
|
|
|
const nodes = [];
|
|
|
|
let pos = 0;
|
2015-06-07 00:31:38 +02:00
|
|
|
for (;;) {
|
2019-01-12 22:36:20 +01:00
|
|
|
const match = rePlaceholder.exec(template);
|
|
|
|
if ( match === null ) { break; }
|
2015-06-07 00:31:38 +02:00
|
|
|
if ( pos !== match.index ) {
|
|
|
|
nodes.push(document.createTextNode(template.slice(pos, match.index)));
|
|
|
|
}
|
|
|
|
pos = rePlaceholder.lastIndex;
|
2019-01-12 22:36:20 +01:00
|
|
|
let select, option;
|
2015-06-07 00:31:38 +02:00
|
|
|
switch ( match[0] ) {
|
|
|
|
case '{{br}}':
|
|
|
|
nodes.push(document.createElement('br'));
|
|
|
|
break;
|
2015-05-21 20:15:17 +02:00
|
|
|
|
2015-06-07 00:31:38 +02:00
|
|
|
case '{{action}}':
|
|
|
|
select = document.createElement('select');
|
|
|
|
select.className = 'static action';
|
|
|
|
option = document.createElement('option');
|
|
|
|
option.setAttribute('value', '');
|
|
|
|
option.textContent = vAPI.i18n('loggerStaticFilteringSentencePartBlock');
|
|
|
|
select.appendChild(option);
|
|
|
|
option = document.createElement('option');
|
|
|
|
option.setAttribute('value', '@@');
|
|
|
|
option.textContent = vAPI.i18n('loggerStaticFilteringSentencePartAllow');
|
|
|
|
select.appendChild(option);
|
|
|
|
nodes.push(select);
|
|
|
|
break;
|
2015-05-21 20:15:17 +02:00
|
|
|
|
2015-06-07 00:31:38 +02:00
|
|
|
case '{{type}}':
|
|
|
|
select = document.createElement('select');
|
|
|
|
select.className = 'static type';
|
|
|
|
option = document.createElement('option');
|
|
|
|
option.setAttribute('value', targetType);
|
|
|
|
option.textContent = vAPI.i18n('loggerStaticFilteringSentencePartType').replace('{{type}}', targetType);
|
|
|
|
select.appendChild(option);
|
|
|
|
option = document.createElement('option');
|
|
|
|
option.setAttribute('value', '');
|
|
|
|
option.textContent = vAPI.i18n('loggerStaticFilteringSentencePartAnyType');
|
|
|
|
select.appendChild(option);
|
|
|
|
nodes.push(select);
|
|
|
|
break;
|
2015-05-25 00:50:09 +02:00
|
|
|
|
2015-06-07 00:31:38 +02:00
|
|
|
case '{{url}}':
|
|
|
|
select = document.createElement('select');
|
|
|
|
select.className = 'static url';
|
2019-05-19 23:00:49 +02:00
|
|
|
for ( const targetURL of targetURLs ) {
|
|
|
|
const value = targetURL.replace(/^[a-z-]+:\/\//, '');
|
2015-06-07 00:31:38 +02:00
|
|
|
option = document.createElement('option');
|
|
|
|
option.setAttribute('value', value);
|
2016-03-28 15:31:53 +02:00
|
|
|
option.textContent = shortenLongString(value, 128);
|
2015-06-07 00:31:38 +02:00
|
|
|
select.appendChild(option);
|
|
|
|
}
|
|
|
|
nodes.push(select);
|
|
|
|
break;
|
2015-05-21 20:15:17 +02:00
|
|
|
|
2015-06-07 00:31:38 +02:00
|
|
|
case '{{origin}}':
|
|
|
|
select = document.createElement('select');
|
|
|
|
select.className = 'static origin';
|
|
|
|
fillOriginSelect(select, targetFrameHostname, targetFrameDomain);
|
|
|
|
option = document.createElement('option');
|
|
|
|
option.setAttribute('value', '');
|
|
|
|
option.textContent = vAPI.i18n('loggerStaticFilteringSentencePartAnyOrigin');
|
|
|
|
select.appendChild(option);
|
|
|
|
nodes.push(select);
|
|
|
|
break;
|
2015-05-25 00:50:09 +02:00
|
|
|
|
2015-06-07 00:31:38 +02:00
|
|
|
case '{{importance}}':
|
|
|
|
select = document.createElement('select');
|
|
|
|
select.className = 'static importance';
|
|
|
|
option = document.createElement('option');
|
|
|
|
option.setAttribute('value', '');
|
|
|
|
option.textContent = vAPI.i18n('loggerStaticFilteringSentencePartNotImportant');
|
|
|
|
select.appendChild(option);
|
|
|
|
option = document.createElement('option');
|
|
|
|
option.setAttribute('value', 'important');
|
|
|
|
option.textContent = vAPI.i18n('loggerStaticFilteringSentencePartImportant');
|
|
|
|
select.appendChild(option);
|
|
|
|
nodes.push(select);
|
|
|
|
break;
|
2015-05-21 20:15:17 +02:00
|
|
|
|
2015-06-07 00:31:38 +02:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( pos < template.length ) {
|
|
|
|
nodes.push(document.createTextNode(template.slice(pos)));
|
2015-05-21 20:15:17 +02:00
|
|
|
}
|
2019-01-12 22:36:20 +01:00
|
|
|
const parent = dialog.querySelector('div.panes > .static > div:first-of-type');
|
|
|
|
for ( let i = 0; i < nodes.length; i++ ) {
|
2015-06-07 00:31:38 +02:00
|
|
|
parent.appendChild(nodes[i]);
|
|
|
|
}
|
|
|
|
parseStaticInputs();
|
|
|
|
};
|
2015-05-21 20:15:17 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const fillDialog = function(domains) {
|
|
|
|
dialog = modalDialog.create(
|
|
|
|
'#netFilteringDialog',
|
|
|
|
( ) => {
|
|
|
|
targetURLs = [];
|
|
|
|
targetRow = null;
|
|
|
|
dialog = null;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
dialog.classList.toggle(
|
2021-03-13 14:53:34 +01:00
|
|
|
'extendedRealm',
|
|
|
|
targetRow.classList.contains('extendedRealm')
|
2019-01-12 22:36:20 +01:00
|
|
|
);
|
2015-06-07 00:31:38 +02:00
|
|
|
targetDomain = domains[0];
|
|
|
|
targetPageDomain = domains[1];
|
|
|
|
targetFrameDomain = domains[2];
|
|
|
|
createPreview(targetType, targetURLs[0]);
|
2019-01-12 22:36:20 +01:00
|
|
|
fillSummaryPane();
|
2015-06-07 00:31:38 +02:00
|
|
|
fillDynamicPane();
|
|
|
|
fillStaticPane();
|
2019-09-24 23:05:03 +02:00
|
|
|
dialog.addEventListener('click', ev => { onClick(ev); }, true);
|
2019-01-12 22:36:20 +01:00
|
|
|
dialog.addEventListener('change', onSelectChange, true);
|
|
|
|
dialog.addEventListener('input', onInputChange, true);
|
|
|
|
modalDialog.show();
|
|
|
|
};
|
|
|
|
|
2019-09-17 21:15:01 +02:00
|
|
|
const toggleOn = async function(ev) {
|
2019-01-12 22:36:20 +01:00
|
|
|
targetRow = ev.target.closest('.canDetails');
|
|
|
|
if ( targetRow === null ) { return; }
|
|
|
|
ev.stopPropagation();
|
|
|
|
targetTabId = tabIdFromAttribute(targetRow);
|
|
|
|
targetType = targetRow.children[5].textContent.trim() || '';
|
|
|
|
targetURLs = createTargetURLs(targetRow.children[6].textContent);
|
2018-12-13 18:30:54 +01:00
|
|
|
targetPageHostname = targetRow.getAttribute('data-tabhn') || '';
|
|
|
|
targetFrameHostname = targetRow.getAttribute('data-dochn') || '';
|
2015-06-07 00:31:38 +02:00
|
|
|
|
|
|
|
// We need the root domain names for best user experience.
|
2019-09-17 21:15:01 +02:00
|
|
|
const domains = await messaging.send('loggerUI', {
|
|
|
|
what: 'getDomainNames',
|
|
|
|
targets: [
|
|
|
|
targetURLs[0],
|
|
|
|
targetPageHostname,
|
|
|
|
targetFrameHostname
|
|
|
|
],
|
|
|
|
});
|
|
|
|
fillDialog(domains);
|
2015-05-21 20:15:17 +02:00
|
|
|
};
|
|
|
|
|
2019-01-14 20:57:31 +01:00
|
|
|
uDom('#netInspector').on(
|
|
|
|
'click',
|
|
|
|
'.canDetails > span:nth-of-type(2),.canDetails > span:nth-of-type(3),.canDetails > span:nth-of-type(5)',
|
2019-09-17 21:15:01 +02:00
|
|
|
ev => { toggleOn(ev); }
|
2019-01-14 20:57:31 +01:00
|
|
|
);
|
2015-06-11 18:12:23 +02:00
|
|
|
})();
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-05-24 17:18:39 +02:00
|
|
|
const rowFilterer = (( ) => {
|
2018-12-16 21:26:38 +01:00
|
|
|
const userFilters = [];
|
|
|
|
const builtinFilters = [];
|
2019-01-12 22:36:20 +01:00
|
|
|
|
|
|
|
let masterFilterSwitch = true;
|
2018-12-16 21:26:38 +01:00
|
|
|
let filters = [];
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2018-12-13 18:30:54 +01:00
|
|
|
const parseInput = function() {
|
2018-12-16 21:26:38 +01:00
|
|
|
userFilters.length = 0;
|
2018-12-14 17:01:21 +01:00
|
|
|
|
2018-12-18 23:23:03 +01:00
|
|
|
const rawParts =
|
|
|
|
uDom.nodeFromSelector('#filterInput > input')
|
|
|
|
.value
|
|
|
|
.trim()
|
|
|
|
.split(/\s+/);
|
2018-12-14 17:01:21 +01:00
|
|
|
const n = rawParts.length;
|
|
|
|
const reStrs = [];
|
|
|
|
let not = false;
|
|
|
|
for ( let i = 0; i < n; i++ ) {
|
|
|
|
let rawPart = rawParts[i];
|
2015-05-23 15:27:24 +02:00
|
|
|
if ( rawPart.charAt(0) === '!' ) {
|
|
|
|
if ( reStrs.length === 0 ) {
|
|
|
|
not = true;
|
|
|
|
}
|
|
|
|
rawPart = rawPart.slice(1);
|
|
|
|
}
|
2018-12-14 17:01:21 +01:00
|
|
|
let reStr = '';
|
|
|
|
if ( rawPart.startsWith('/') && rawPart.endsWith('/') ) {
|
|
|
|
reStr = rawPart.slice(1, -1);
|
|
|
|
try {
|
|
|
|
new RegExp(reStr);
|
|
|
|
} catch(ex) {
|
|
|
|
reStr = '';
|
|
|
|
}
|
2015-05-09 00:28:01 +02:00
|
|
|
}
|
2018-12-14 17:01:21 +01:00
|
|
|
if ( reStr === '' ) {
|
|
|
|
const hardBeg = rawPart.startsWith('|');
|
|
|
|
if ( hardBeg ) {
|
|
|
|
rawPart = rawPart.slice(1);
|
|
|
|
}
|
|
|
|
const hardEnd = rawPart.endsWith('|');
|
|
|
|
if ( hardEnd ) {
|
|
|
|
rawPart = rawPart.slice(0, -1);
|
|
|
|
}
|
|
|
|
// https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions
|
|
|
|
reStr = rawPart.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
2018-12-18 19:37:01 +01:00
|
|
|
// https://github.com/orgs/uBlockOrigin/teams/ublock-issues-volunteers/discussions/51
|
|
|
|
// Be more flexible when interpreting leading/trailing pipes,
|
|
|
|
// as leading/trailing pipes are often used in static filters.
|
2018-12-14 17:01:21 +01:00
|
|
|
if ( hardBeg ) {
|
2018-12-18 19:37:01 +01:00
|
|
|
reStr = reStr !== '' ? '(?:^|\\s|\\|)' + reStr : '\\|';
|
2018-12-14 17:01:21 +01:00
|
|
|
}
|
|
|
|
if ( hardEnd ) {
|
2018-12-18 19:37:01 +01:00
|
|
|
reStr += '(?:\\||\\s|$)';
|
2018-12-14 17:01:21 +01:00
|
|
|
}
|
2015-05-09 00:28:01 +02:00
|
|
|
}
|
2018-12-19 20:05:19 +01:00
|
|
|
if ( reStr === '' ) { continue; }
|
2015-05-23 15:27:24 +02:00
|
|
|
reStrs.push(reStr);
|
2015-05-28 02:48:04 +02:00
|
|
|
if ( i < (n - 1) && rawParts[i + 1] === '||' ) {
|
|
|
|
i += 1;
|
2015-05-23 15:27:24 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
reStr = reStrs.length === 1 ? reStrs[0] : reStrs.join('|');
|
2018-12-16 21:26:38 +01:00
|
|
|
userFilters.push({
|
2015-05-09 00:28:01 +02:00
|
|
|
re: new RegExp(reStr, 'i'),
|
|
|
|
r: !not
|
|
|
|
});
|
2018-12-14 17:01:21 +01:00
|
|
|
reStrs.length = 0;
|
2015-05-23 15:27:24 +02:00
|
|
|
not = false;
|
2015-05-09 00:28:01 +02:00
|
|
|
}
|
2018-12-16 21:26:38 +01:00
|
|
|
filters = builtinFilters.concat(userFilters);
|
2015-05-09 00:28:01 +02:00
|
|
|
};
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const filterOne = function(logEntry) {
|
|
|
|
if (
|
|
|
|
logEntry.dead ||
|
|
|
|
selectedTabId !== 0 &&
|
2019-01-16 19:29:34 +01:00
|
|
|
(
|
|
|
|
logEntry.tabId === undefined ||
|
|
|
|
logEntry.tabId > 0 && logEntry.tabId !== selectedTabId
|
|
|
|
)
|
2019-01-12 22:36:20 +01:00
|
|
|
) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( masterFilterSwitch === false || filters.length === 0 ) {
|
|
|
|
return true;
|
2015-05-09 00:28:01 +02:00
|
|
|
}
|
2019-01-12 22:36:20 +01:00
|
|
|
|
|
|
|
// Do not filter out tab load event, they help separate key sections
|
|
|
|
// of logger.
|
|
|
|
if ( logEntry.type === 'tabLoad' ) { return true; }
|
|
|
|
|
2018-12-14 17:01:21 +01:00
|
|
|
for ( const f of filters ) {
|
2019-01-12 22:36:20 +01:00
|
|
|
if ( f.re.test(logEntry.textContent) !== f.r ) { return false; }
|
2015-05-09 00:28:01 +02:00
|
|
|
}
|
2019-01-12 22:36:20 +01:00
|
|
|
return true;
|
2015-05-09 00:28:01 +02:00
|
|
|
};
|
|
|
|
|
2018-12-13 18:30:54 +01:00
|
|
|
const filterAll = function() {
|
2019-01-12 22:36:20 +01:00
|
|
|
filteredLoggerEntries = [];
|
|
|
|
filteredLoggerEntryVoidedCount = 0;
|
|
|
|
for ( const entry of loggerEntries ) {
|
|
|
|
if ( filterOne(entry) === false ) { continue; }
|
|
|
|
filteredLoggerEntries.push(entry);
|
|
|
|
if ( entry.voided ) {
|
|
|
|
filteredLoggerEntryVoidedCount += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
viewPort.updateContent(0);
|
2018-12-19 20:05:19 +01:00
|
|
|
uDom.nodeFromId('filterButton').classList.toggle(
|
|
|
|
'active',
|
2019-01-12 22:36:20 +01:00
|
|
|
filters.length !== 0
|
|
|
|
);
|
|
|
|
uDom.nodeFromId('clean').classList.toggle(
|
|
|
|
'disabled',
|
|
|
|
filteredLoggerEntryVoidedCount === 0
|
|
|
|
);
|
|
|
|
uDom.nodeFromId('clear').classList.toggle(
|
|
|
|
'disabled',
|
|
|
|
filteredLoggerEntries.length === 0
|
2018-12-19 20:05:19 +01:00
|
|
|
);
|
2015-05-09 00:28:01 +02:00
|
|
|
};
|
|
|
|
|
2019-11-19 18:05:33 +01:00
|
|
|
const onFilterChangedAsync = (( ) => {
|
2018-12-14 17:01:21 +01:00
|
|
|
let timer;
|
|
|
|
const commit = ( ) => {
|
|
|
|
timer = undefined;
|
2015-05-09 00:28:01 +02:00
|
|
|
parseInput();
|
|
|
|
filterAll();
|
|
|
|
};
|
2019-11-19 18:05:33 +01:00
|
|
|
return ( ) => {
|
2018-12-14 17:01:21 +01:00
|
|
|
if ( timer !== undefined ) {
|
2015-05-09 00:28:01 +02:00
|
|
|
clearTimeout(timer);
|
|
|
|
}
|
2015-05-17 19:02:56 +02:00
|
|
|
timer = vAPI.setTimeout(commit, 750);
|
2015-05-09 00:28:01 +02:00
|
|
|
};
|
|
|
|
})();
|
|
|
|
|
2018-12-13 18:30:54 +01:00
|
|
|
const onFilterButton = function() {
|
2019-01-12 22:36:20 +01:00
|
|
|
masterFilterSwitch = !masterFilterSwitch;
|
|
|
|
uDom.nodeFromId('netInspector').classList.toggle(
|
|
|
|
'f',
|
|
|
|
masterFilterSwitch
|
|
|
|
);
|
|
|
|
filterAll();
|
2015-05-09 00:28:01 +02:00
|
|
|
};
|
|
|
|
|
2018-12-16 21:26:38 +01:00
|
|
|
const onToggleExtras = function(ev) {
|
|
|
|
ev.target.classList.toggle('expanded');
|
|
|
|
};
|
|
|
|
|
|
|
|
const onToggleBuiltinExpression = function(ev) {
|
|
|
|
builtinFilters.length = 0;
|
|
|
|
|
|
|
|
ev.target.classList.toggle('on');
|
|
|
|
const filtexElems = ev.currentTarget.querySelectorAll('[data-filtex]');
|
|
|
|
const orExprs = [];
|
|
|
|
let not = false;
|
|
|
|
for ( const filtexElem of filtexElems ) {
|
|
|
|
let filtex = filtexElem.getAttribute('data-filtex');
|
|
|
|
let active = filtexElem.classList.contains('on');
|
|
|
|
if ( filtex === '!' ) {
|
|
|
|
if ( orExprs.length !== 0 ) {
|
|
|
|
builtinFilters.push({
|
|
|
|
re: new RegExp(orExprs.join('|')),
|
|
|
|
r: !not
|
|
|
|
});
|
|
|
|
orExprs.length = 0;
|
|
|
|
}
|
|
|
|
not = active;
|
|
|
|
} else if ( active ) {
|
|
|
|
orExprs.push(filtex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( orExprs.length !== 0 ) {
|
|
|
|
builtinFilters.push({
|
|
|
|
re: new RegExp(orExprs.join('|')),
|
|
|
|
r: !not
|
|
|
|
});
|
|
|
|
}
|
2018-12-19 20:05:19 +01:00
|
|
|
filters = builtinFilters.concat(userFilters);
|
2018-12-16 21:26:38 +01:00
|
|
|
uDom.nodeFromId('filterExprButton').classList.toggle(
|
2018-12-19 20:05:19 +01:00
|
|
|
'active',
|
2018-12-16 21:26:38 +01:00
|
|
|
builtinFilters.length !== 0
|
|
|
|
);
|
|
|
|
filterAll();
|
|
|
|
};
|
|
|
|
|
2015-05-09 00:28:01 +02:00
|
|
|
uDom('#filterButton').on('click', onFilterButton);
|
2018-12-18 23:23:03 +01:00
|
|
|
uDom('#filterInput > input').on('input', onFilterChangedAsync);
|
2018-12-16 21:26:38 +01:00
|
|
|
uDom('#filterExprButton').on('click', onToggleExtras);
|
|
|
|
uDom('#filterExprPicker').on('click', '[data-filtex]', onToggleBuiltinExpression);
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2015-06-26 01:09:47 +02:00
|
|
|
// https://github.com/gorhill/uBlock/issues/404
|
2018-12-16 21:26:38 +01:00
|
|
|
// Ensure page state is in sync with the state of its various widgets.
|
2015-06-26 01:09:47 +02:00
|
|
|
parseInput();
|
|
|
|
filterAll();
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
return { filterOne, filterAll };
|
2015-05-09 00:28:01 +02:00
|
|
|
})();
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
// Discard logger entries to prevent undue memory usage growth. The criteria
|
|
|
|
// to discard are multiple and user configurable:
|
|
|
|
//
|
|
|
|
// - Max number of page load per distinct tab
|
|
|
|
// - Max number of entry per distinct tab
|
|
|
|
// - Max entry age
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2019-05-24 17:18:39 +02:00
|
|
|
const rowJanitor = (( ) => {
|
2019-01-12 22:36:20 +01:00
|
|
|
const tabIdToDiscard = new Set();
|
|
|
|
const tabIdToLoadCountMap = new Map();
|
|
|
|
const tabIdToEntryCountMap = new Map();
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
let rowIndex = 0;
|
|
|
|
|
|
|
|
const discard = function(timeRemaining) {
|
|
|
|
const opts = loggerSettings.discard;
|
|
|
|
const maxLoadCount = typeof opts.maxLoadCount === 'number'
|
|
|
|
? opts.maxLoadCount
|
|
|
|
: 0;
|
|
|
|
const maxEntryCount = typeof opts.maxEntryCount === 'number'
|
|
|
|
? opts.maxEntryCount
|
|
|
|
: 0;
|
|
|
|
const obsolete = typeof opts.maxAge === 'number'
|
|
|
|
? Date.now() - opts.maxAge * 60000
|
|
|
|
: 0;
|
|
|
|
const deadline = Date.now() + Math.ceil(timeRemaining);
|
|
|
|
|
|
|
|
let i = rowIndex;
|
|
|
|
// TODO: below should not happen -- remove when confirmed.
|
|
|
|
if ( i >= loggerEntries.length ) {
|
|
|
|
i = 0;
|
2015-06-15 02:11:25 +02:00
|
|
|
}
|
2015-05-10 18:25:26 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
if ( i === 0 ) {
|
|
|
|
tabIdToDiscard.clear();
|
|
|
|
tabIdToLoadCountMap.clear();
|
|
|
|
tabIdToEntryCountMap.clear();
|
|
|
|
}
|
2015-05-10 18:25:26 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
let idel = -1;
|
|
|
|
let bufferedTabId = 0;
|
|
|
|
let bufferedEntryCount = 0;
|
|
|
|
let modified = false;
|
|
|
|
|
|
|
|
while ( i < loggerEntries.length ) {
|
|
|
|
|
|
|
|
if ( i % 64 === 0 && Date.now() >= deadline ) { break; }
|
|
|
|
|
|
|
|
const entry = loggerEntries[i];
|
|
|
|
const tabId = entry.tabId || 0;
|
|
|
|
|
|
|
|
if ( entry.dead || tabIdToDiscard.has(tabId) ) {
|
|
|
|
if ( idel === -1 ) { idel = i; }
|
|
|
|
i += 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( maxLoadCount !== 0 && entry.type === 'tabLoad' ) {
|
|
|
|
let count = (tabIdToLoadCountMap.get(tabId) || 0) + 1;
|
|
|
|
tabIdToLoadCountMap.set(tabId, count);
|
|
|
|
if ( count >= maxLoadCount ) {
|
|
|
|
tabIdToDiscard.add(tabId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( maxEntryCount !== 0 ) {
|
|
|
|
if ( bufferedTabId !== tabId ) {
|
|
|
|
if ( bufferedEntryCount !== 0 ) {
|
|
|
|
tabIdToEntryCountMap.set(bufferedTabId, bufferedEntryCount);
|
|
|
|
}
|
|
|
|
bufferedTabId = tabId;
|
|
|
|
bufferedEntryCount = tabIdToEntryCountMap.get(tabId) || 0;
|
|
|
|
}
|
|
|
|
bufferedEntryCount += 1;
|
|
|
|
if ( bufferedEntryCount >= maxEntryCount ) {
|
|
|
|
tabIdToDiscard.add(bufferedTabId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Since entries in the logger are chronologically ordered,
|
|
|
|
// everything below obsolete is to be discarded.
|
|
|
|
if ( obsolete !== 0 && entry.tstamp <= obsolete ) {
|
|
|
|
if ( idel === -1 ) { idel = i; }
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( idel !== -1 ) {
|
|
|
|
loggerEntries.copyWithin(idel, i);
|
|
|
|
loggerEntries.length -= i - idel;
|
|
|
|
idel = -1;
|
|
|
|
modified = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( idel !== -1 ) {
|
|
|
|
loggerEntries.length = idel;
|
|
|
|
modified = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( i >= loggerEntries.length ) { i = 0; }
|
|
|
|
rowIndex = i;
|
|
|
|
|
|
|
|
if ( rowIndex === 0 ) {
|
|
|
|
tabIdToDiscard.clear();
|
|
|
|
tabIdToLoadCountMap.clear();
|
|
|
|
tabIdToEntryCountMap.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( modified === false ) { return; }
|
|
|
|
|
|
|
|
rowFilterer.filterAll();
|
|
|
|
};
|
|
|
|
|
|
|
|
const discardAsync = function() {
|
|
|
|
setTimeout(
|
|
|
|
( ) => {
|
|
|
|
self.requestIdleCallback(deadline => {
|
|
|
|
discard(deadline.timeRemaining());
|
|
|
|
discardAsync();
|
|
|
|
});
|
|
|
|
},
|
|
|
|
1889
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
// Clear voided entries from the logger's visible content.
|
|
|
|
//
|
|
|
|
// Voided entries should be visible only from the "All" option of the
|
|
|
|
// tab selector.
|
|
|
|
//
|
|
|
|
const clean = function() {
|
|
|
|
if ( filteredLoggerEntries.length === 0 ) { return; }
|
|
|
|
|
|
|
|
let j = 0;
|
|
|
|
let targetEntry = filteredLoggerEntries[0];
|
|
|
|
for ( const entry of loggerEntries ) {
|
|
|
|
if ( entry !== targetEntry ) { continue; }
|
|
|
|
if ( entry.voided ) {
|
|
|
|
entry.dead = true;
|
|
|
|
}
|
|
|
|
j += 1;
|
|
|
|
if ( j === filteredLoggerEntries.length ) { break; }
|
|
|
|
targetEntry = filteredLoggerEntries[j];
|
|
|
|
}
|
|
|
|
rowFilterer.filterAll();
|
|
|
|
};
|
|
|
|
|
|
|
|
// Clear the logger's visible content.
|
|
|
|
//
|
|
|
|
// "Unrelated" entries -- shown for convenience -- will be also cleared
|
|
|
|
// if and only if the filtered logger content is made entirely of unrelated
|
|
|
|
// entries. In effect, this means clicking a second time on the eraser will
|
|
|
|
// cause unrelated entries to also be cleared.
|
|
|
|
//
|
|
|
|
const clear = function() {
|
|
|
|
if ( filteredLoggerEntries.length === 0 ) { return; }
|
|
|
|
|
|
|
|
let clearUnrelated = true;
|
|
|
|
if ( selectedTabId !== 0 ) {
|
|
|
|
for ( const entry of filteredLoggerEntries ) {
|
|
|
|
if ( entry.tabId === selectedTabId ) {
|
|
|
|
clearUnrelated = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let j = 0;
|
|
|
|
let targetEntry = filteredLoggerEntries[0];
|
|
|
|
for ( const entry of loggerEntries ) {
|
|
|
|
if ( entry !== targetEntry ) { continue; }
|
|
|
|
if ( entry.tabId === selectedTabId || clearUnrelated ) {
|
|
|
|
entry.dead = true;
|
|
|
|
}
|
|
|
|
j += 1;
|
|
|
|
if ( j === filteredLoggerEntries.length ) { break; }
|
|
|
|
targetEntry = filteredLoggerEntries[j];
|
|
|
|
}
|
|
|
|
rowFilterer.filterAll();
|
|
|
|
};
|
|
|
|
|
|
|
|
discardAsync();
|
|
|
|
|
|
|
|
uDom.nodeFromId('clean').addEventListener('click', clean);
|
|
|
|
uDom.nodeFromId('clear').addEventListener('click', clear);
|
|
|
|
|
|
|
|
return {
|
|
|
|
inserted: function(count) {
|
|
|
|
if ( rowIndex !== 0 ) {
|
|
|
|
rowIndex += count;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
};
|
|
|
|
})();
|
2015-05-09 00:28:01 +02:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2018-12-17 19:54:17 +01:00
|
|
|
const pauseNetInspector = function() {
|
|
|
|
netInspectorPaused = uDom.nodeFromId('netInspector')
|
|
|
|
.classList
|
|
|
|
.toggle('paused');
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
const toggleVCompactView = function() {
|
2019-01-12 22:36:20 +01:00
|
|
|
uDom.nodeFromSelector('#netInspector .vCompactToggler')
|
|
|
|
.classList
|
|
|
|
.toggle('vExpanded');
|
|
|
|
viewPort.updateLayout();
|
2015-07-01 15:19:13 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-05-24 17:18:39 +02:00
|
|
|
const popupManager = (( ) => {
|
2018-12-13 18:30:54 +01:00
|
|
|
let realTabId = 0;
|
|
|
|
let popup = null;
|
|
|
|
let popupObserver = null;
|
|
|
|
|
|
|
|
const resizePopup = function() {
|
|
|
|
if ( popup === null ) { return; }
|
|
|
|
const popupBody = popup.contentWindow.document.body;
|
|
|
|
if ( popupBody.clientWidth !== 0 && popup.clientWidth !== popupBody.clientWidth ) {
|
|
|
|
popup.style.setProperty('width', popupBody.clientWidth + 'px');
|
2015-05-09 00:28:01 +02:00
|
|
|
}
|
|
|
|
if ( popupBody.clientHeight !== 0 && popup.clientHeight !== popupBody.clientHeight ) {
|
2015-05-10 15:28:50 +02:00
|
|
|
popup.style.setProperty('height', popupBody.clientHeight + 'px');
|
2015-05-09 00:28:01 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-12-13 18:30:54 +01:00
|
|
|
const onLoad = function() {
|
2015-05-09 00:28:01 +02:00
|
|
|
resizePopup();
|
|
|
|
popupObserver.observe(popup.contentDocument.body, {
|
|
|
|
subtree: true,
|
|
|
|
attributes: true
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2018-12-13 18:30:54 +01:00
|
|
|
const setTabId = function(tabId) {
|
|
|
|
if ( popup === null ) { return; }
|
2020-08-21 14:23:36 +02:00
|
|
|
popup.setAttribute('src', 'popup-fenix.html?portrait=1&tabId=' + tabId);
|
2018-12-13 18:30:54 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
const onTabIdChanged = function() {
|
|
|
|
const tabId = tabIdFromPageSelector();
|
|
|
|
if ( tabId === 0 ) { return toggleOff(); }
|
|
|
|
realTabId = tabId;
|
|
|
|
setTabId(realTabId);
|
|
|
|
};
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2018-12-13 18:30:54 +01:00
|
|
|
const toggleOn = function() {
|
|
|
|
const tabId = tabIdFromPageSelector();
|
|
|
|
if ( tabId === 0 ) { return; }
|
|
|
|
realTabId = tabId;
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2018-12-13 18:30:54 +01:00
|
|
|
popup = uDom.nodeFromId('popupContainer');
|
2015-05-09 00:28:01 +02:00
|
|
|
|
|
|
|
popup.addEventListener('load', onLoad);
|
|
|
|
popupObserver = new MutationObserver(resizePopup);
|
|
|
|
|
2018-12-13 18:30:54 +01:00
|
|
|
const parent = uDom.nodeFromId('inspectors');
|
|
|
|
const rect = parent.getBoundingClientRect();
|
2019-01-12 22:36:20 +01:00
|
|
|
popup.style.setProperty('right', `${rect.right - parent.clientWidth}px`);
|
2015-06-26 06:08:41 +02:00
|
|
|
parent.classList.add('popupOn');
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2018-12-13 18:30:54 +01:00
|
|
|
document.addEventListener('tabIdChanged', onTabIdChanged);
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2018-12-13 18:30:54 +01:00
|
|
|
setTabId(realTabId);
|
2018-12-19 22:21:23 +01:00
|
|
|
uDom.nodeFromId('showpopup').classList.add('active');
|
2018-12-13 18:30:54 +01:00
|
|
|
};
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2018-12-13 18:30:54 +01:00
|
|
|
const toggleOff = function() {
|
2018-12-19 22:21:23 +01:00
|
|
|
uDom.nodeFromId('showpopup').classList.remove('active');
|
2018-12-13 18:30:54 +01:00
|
|
|
document.removeEventListener('tabIdChanged', onTabIdChanged);
|
|
|
|
uDom.nodeFromId('inspectors').classList.remove('popupOn');
|
2015-05-09 00:28:01 +02:00
|
|
|
popup.removeEventListener('load', onLoad);
|
|
|
|
popupObserver.disconnect();
|
|
|
|
popupObserver = null;
|
|
|
|
popup.setAttribute('src', '');
|
2018-12-13 18:30:54 +01:00
|
|
|
|
|
|
|
realTabId = 0;
|
2015-05-09 00:28:01 +02:00
|
|
|
};
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
const api = {
|
|
|
|
get tabId() { return realTabId || 0; },
|
2015-05-09 00:28:01 +02:00
|
|
|
toggleOff: function() {
|
2018-12-13 18:30:54 +01:00
|
|
|
if ( realTabId !== 0 ) {
|
2015-05-09 00:28:01 +02:00
|
|
|
toggleOff();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
uDom.nodeFromId('showpopup').addEventListener(
|
|
|
|
'click',
|
|
|
|
( ) => {
|
|
|
|
void (realTabId === 0 ? toggleOn() : toggleOff());
|
|
|
|
}
|
|
|
|
);
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
return api;
|
|
|
|
})();
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-05-24 17:18:39 +02:00
|
|
|
// Filter hit stats' MVP ("minimum viable product")
|
|
|
|
//
|
|
|
|
const loggerStats = (( ) => {
|
2021-03-13 14:53:34 +01:00
|
|
|
const enabled = false;
|
2019-05-24 17:18:39 +02:00
|
|
|
const filterHits = new Map();
|
|
|
|
let dialog;
|
|
|
|
let timer;
|
|
|
|
const makeRow = function() {
|
|
|
|
const div = document.createElement('div');
|
|
|
|
div.appendChild(document.createElement('span'));
|
|
|
|
div.appendChild(document.createElement('span'));
|
|
|
|
return div;
|
|
|
|
};
|
|
|
|
|
|
|
|
const fillRow = function(div, entry) {
|
|
|
|
div.children[0].textContent = entry[1].toLocaleString();
|
|
|
|
div.children[1].textContent = entry[0];
|
|
|
|
};
|
|
|
|
|
|
|
|
const updateList = function() {
|
|
|
|
const sortedHits = Array.from(filterHits).sort((a, b) => {
|
|
|
|
return b[1] - a[1];
|
|
|
|
});
|
|
|
|
|
|
|
|
const doc = document;
|
|
|
|
const parent = dialog.querySelector('.sortedEntries');
|
|
|
|
let i = 0;
|
|
|
|
|
|
|
|
// Reuse existing rows
|
|
|
|
for ( let iRow = 0; iRow < parent.childElementCount; iRow++ ) {
|
|
|
|
if ( i === sortedHits.length ) { break; }
|
|
|
|
fillRow(parent.children[iRow], sortedHits[i]);
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append new rows
|
|
|
|
if ( i < sortedHits.length ) {
|
|
|
|
const list = doc.createDocumentFragment();
|
|
|
|
for ( ; i < sortedHits.length; i++ ) {
|
|
|
|
const div = makeRow();
|
|
|
|
fillRow(div, sortedHits[i]);
|
|
|
|
list.appendChild(div);
|
|
|
|
}
|
|
|
|
parent.appendChild(list);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove extraneous rows
|
|
|
|
// [Should never happen at this point in this current
|
|
|
|
// bare-bone implementation]
|
|
|
|
};
|
|
|
|
|
|
|
|
const toggleOn = function() {
|
|
|
|
dialog = modalDialog.create(
|
|
|
|
'#loggerStatsDialog',
|
|
|
|
( ) => {
|
|
|
|
dialog = undefined;
|
|
|
|
if ( timer !== undefined ) {
|
|
|
|
self.cancelIdleCallback(timer);
|
|
|
|
timer = undefined;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
updateList();
|
|
|
|
modalDialog.show();
|
|
|
|
};
|
|
|
|
|
|
|
|
uDom.nodeFromId('loggerStats').addEventListener('click', toggleOn);
|
|
|
|
|
|
|
|
return {
|
|
|
|
processFilter: function(filter) {
|
2021-03-13 14:53:34 +01:00
|
|
|
if ( enabled !== true ) { return; }
|
2019-05-24 17:18:39 +02:00
|
|
|
if ( filter.source !== 'static' && filter.source !== 'cosmetic' ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
filterHits.set(filter.raw, (filterHits.get(filter.raw) || 0) + 1);
|
|
|
|
if ( dialog === undefined || timer !== undefined ) { return; }
|
|
|
|
timer = self.requestIdleCallback(
|
|
|
|
( ) => {
|
|
|
|
timer = undefined;
|
|
|
|
updateList();
|
|
|
|
},
|
|
|
|
{ timeout: 2001 }
|
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
})();
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
(( ) => {
|
2019-01-14 20:57:31 +01:00
|
|
|
const lines = [];
|
|
|
|
const options = {
|
|
|
|
format: 'list',
|
|
|
|
encoding: 'markdown',
|
|
|
|
time: 'anonymous',
|
|
|
|
};
|
|
|
|
let dialog;
|
|
|
|
|
|
|
|
const collectLines = function() {
|
|
|
|
lines.length = 0;
|
|
|
|
let t0 = filteredLoggerEntries.length !== 0
|
|
|
|
? filteredLoggerEntries[filteredLoggerEntries.length - 1].tstamp
|
|
|
|
: 0;
|
|
|
|
for ( const entry of filteredLoggerEntries ) {
|
|
|
|
const text = entry.textContent;
|
|
|
|
const fields = [];
|
|
|
|
let i = 0;
|
|
|
|
let beg = text.indexOf('\t');
|
|
|
|
if ( beg === 0 ) { continue; }
|
|
|
|
let timeField = text.slice(0, beg);
|
|
|
|
if ( options.time === 'anonymous' ) {
|
|
|
|
timeField = '+' + Math.round((entry.tstamp - t0) / 1000).toString();
|
|
|
|
}
|
|
|
|
fields.push(timeField);
|
|
|
|
beg += 1;
|
|
|
|
while ( beg < text.length ) {
|
|
|
|
let end = text.indexOf('\t', beg);
|
|
|
|
if ( end === -1 ) { end = text.length; }
|
|
|
|
fields.push(text.slice(beg, end));
|
|
|
|
beg = end + 1;
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
lines.push(fields);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const formatAsPlainTextTable = function() {
|
|
|
|
const outputAll = [];
|
|
|
|
for ( const fields of lines ) {
|
|
|
|
outputAll.push(fields.join('\t'));
|
|
|
|
}
|
|
|
|
outputAll.push('');
|
|
|
|
return outputAll.join('\n');
|
|
|
|
};
|
|
|
|
|
|
|
|
const formatAsMarkdownTable = function() {
|
|
|
|
const outputAll = [];
|
|
|
|
let fieldCount = 0;
|
|
|
|
for ( const fields of lines ) {
|
|
|
|
if ( fields.length <= 2 ) { continue; }
|
|
|
|
if ( fields.length > fieldCount ) {
|
|
|
|
fieldCount = fields.length;
|
|
|
|
}
|
|
|
|
const outputOne = [];
|
|
|
|
for ( let i = 0; i < fields.length; i++ ) {
|
|
|
|
const field = fields[i];
|
|
|
|
let code = /\b(?:www\.|https?:\/\/)/.test(field) ? '`' : '';
|
|
|
|
outputOne.push(` ${code}${field.replace(/\|/g, '\\|')}${code} `);
|
|
|
|
}
|
|
|
|
outputAll.push(outputOne.join('|'));
|
|
|
|
}
|
2019-01-14 22:52:13 +01:00
|
|
|
if ( fieldCount !== 0 ) {
|
|
|
|
outputAll.unshift(
|
|
|
|
`${' |'.repeat(fieldCount-1)} `,
|
|
|
|
`${':--- |'.repeat(fieldCount-1)}:--- `
|
|
|
|
);
|
|
|
|
}
|
2019-01-14 20:57:31 +01:00
|
|
|
return `<details><summary>Logger output</summary>\n\n|${outputAll.join('|\n|')}|\n</details>\n`;
|
|
|
|
};
|
|
|
|
|
|
|
|
const formatAsTable = function() {
|
|
|
|
if ( options.encoding === 'plain' ) {
|
|
|
|
return formatAsPlainTextTable();
|
|
|
|
}
|
|
|
|
return formatAsMarkdownTable();
|
|
|
|
};
|
|
|
|
|
|
|
|
const formatAsList = function() {
|
|
|
|
const outputAll = [];
|
|
|
|
for ( const fields of lines ) {
|
|
|
|
const outputOne = [];
|
|
|
|
for ( let i = 0; i < fields.length; i++ ) {
|
|
|
|
let str = fields[i];
|
|
|
|
if ( str.length === 0 ) { continue; }
|
|
|
|
outputOne.push(str);
|
|
|
|
}
|
|
|
|
outputAll.push(outputOne.join('\n'));
|
|
|
|
}
|
|
|
|
let before, between, after;
|
|
|
|
if ( options.encoding === 'markdown' ) {
|
|
|
|
const code = '```';
|
|
|
|
before = `<details><summary>Logger output</summary>\n\n${code}\n`;
|
|
|
|
between = `\n${code}\n${code}\n`;
|
|
|
|
after = `\n${code}\n</details>\n`;
|
|
|
|
} else {
|
|
|
|
before = '';
|
|
|
|
between = '\n\n';
|
|
|
|
after = '\n';
|
|
|
|
}
|
|
|
|
return `${before}${outputAll.join(between)}${after}`;
|
|
|
|
};
|
|
|
|
|
|
|
|
const format = function() {
|
|
|
|
const output = dialog.querySelector('.output');
|
|
|
|
if ( options.format === 'list' ) {
|
|
|
|
output.textContent = formatAsList();
|
|
|
|
} else {
|
|
|
|
output.textContent = formatAsTable();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const setRadioButton = function(group, value) {
|
|
|
|
if ( options.hasOwnProperty(group) === false ) { return; }
|
|
|
|
const groupEl = dialog.querySelector(`[data-radio="${group}"]`);
|
|
|
|
const buttonEls = groupEl.querySelectorAll('[data-radio-item]');
|
|
|
|
for ( const buttonEl of buttonEls ) {
|
|
|
|
buttonEl.classList.toggle(
|
|
|
|
'on',
|
|
|
|
buttonEl.getAttribute('data-radio-item') === value
|
|
|
|
);
|
|
|
|
}
|
|
|
|
options[group] = value;
|
|
|
|
};
|
|
|
|
|
|
|
|
const onOption = function(ev) {
|
|
|
|
const target = ev.target.closest('span[data-i18n]');
|
|
|
|
if ( target === null ) { return; }
|
|
|
|
|
|
|
|
// Copy to clipboard
|
|
|
|
if ( target.matches('.pushbutton') ) {
|
|
|
|
const textarea = dialog.querySelector('textarea');
|
|
|
|
textarea.focus();
|
|
|
|
if ( textarea.selectionEnd === textarea.selectionStart ) {
|
|
|
|
textarea.select();
|
|
|
|
}
|
|
|
|
document.execCommand('copy');
|
|
|
|
ev.stopPropagation();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Radio buttons
|
|
|
|
const group = target.closest('[data-radio]');
|
|
|
|
if ( group === null ) { return; }
|
|
|
|
if ( target.matches('span.on') ) { return; }
|
|
|
|
const item = target.closest('[data-radio-item]');
|
|
|
|
if ( item === null ) { return; }
|
|
|
|
setRadioButton(
|
|
|
|
group.getAttribute('data-radio'),
|
|
|
|
item.getAttribute('data-radio-item')
|
|
|
|
);
|
2019-01-14 22:52:13 +01:00
|
|
|
format();
|
2019-01-14 20:57:31 +01:00
|
|
|
ev.stopPropagation();
|
|
|
|
};
|
|
|
|
|
|
|
|
const toggleOn = function() {
|
|
|
|
dialog = modalDialog.create(
|
|
|
|
'#loggerExportDialog',
|
|
|
|
( ) => {
|
|
|
|
dialog = undefined;
|
|
|
|
lines.length = 0;
|
|
|
|
}
|
|
|
|
);
|
2019-01-14 22:52:13 +01:00
|
|
|
|
2019-01-14 20:57:31 +01:00
|
|
|
setRadioButton('format', options.format);
|
|
|
|
setRadioButton('encoding', options.encoding);
|
|
|
|
|
2019-01-14 22:52:13 +01:00
|
|
|
collectLines();
|
|
|
|
format();
|
|
|
|
|
2019-01-14 20:57:31 +01:00
|
|
|
dialog.querySelector('.options').addEventListener(
|
|
|
|
'click',
|
|
|
|
onOption,
|
|
|
|
{ capture: true }
|
|
|
|
);
|
|
|
|
|
|
|
|
modalDialog.show();
|
|
|
|
};
|
|
|
|
|
|
|
|
uDom.nodeFromId('loggerExport').addEventListener('click', toggleOn);
|
|
|
|
})();
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
// TODO:
|
|
|
|
// - Give some thoughts to:
|
|
|
|
// - an option to discard immediately filtered out new entries
|
|
|
|
// - max entry count _per load_
|
|
|
|
//
|
2019-05-24 17:18:39 +02:00
|
|
|
const loggerSettings = (( ) => {
|
2019-01-12 22:36:20 +01:00
|
|
|
const settings = {
|
|
|
|
discard: {
|
|
|
|
maxAge: 240, // global
|
|
|
|
maxEntryCount: 2000, // per-tab
|
|
|
|
maxLoadCount: 20, // per-tab
|
|
|
|
},
|
|
|
|
columns: [ true, true, true, true, true, true, true, true ],
|
|
|
|
linesPerEntry: 4,
|
|
|
|
};
|
|
|
|
|
2020-02-21 21:34:54 +01:00
|
|
|
vAPI.localStorage.getItemAsync('loggerSettings').then(value => {
|
2019-01-12 22:36:20 +01:00
|
|
|
try {
|
2020-02-21 21:34:54 +01:00
|
|
|
const stored = JSON.parse(value);
|
2019-01-12 22:36:20 +01:00
|
|
|
if ( typeof stored.discard.maxAge === 'number' ) {
|
|
|
|
settings.discard.maxAge = stored.discard.maxAge;
|
|
|
|
}
|
|
|
|
if ( typeof stored.discard.maxEntryCount === 'number' ) {
|
|
|
|
settings.discard.maxEntryCount = stored.discard.maxEntryCount;
|
|
|
|
}
|
|
|
|
if ( typeof stored.discard.maxLoadCount === 'number' ) {
|
|
|
|
settings.discard.maxLoadCount = stored.discard.maxLoadCount;
|
|
|
|
}
|
|
|
|
if ( typeof stored.linesPerEntry === 'number' ) {
|
|
|
|
settings.linesPerEntry = stored.linesPerEntry;
|
|
|
|
}
|
|
|
|
if ( Array.isArray(stored.columns) ) {
|
|
|
|
settings.columns = stored.columns;
|
|
|
|
}
|
|
|
|
} catch(ex) {
|
|
|
|
}
|
2020-02-21 21:34:54 +01:00
|
|
|
});
|
2019-01-12 22:36:20 +01:00
|
|
|
|
|
|
|
const valueFromInput = function(input, def) {
|
|
|
|
let value = parseInt(input.value, 10);
|
|
|
|
if ( isNaN(value) ) { value = def; }
|
|
|
|
const min = parseInt(input.getAttribute('min'), 10);
|
|
|
|
if ( isNaN(min) === false ) {
|
|
|
|
value = Math.max(value, min);
|
|
|
|
}
|
|
|
|
const max = parseInt(input.getAttribute('max'), 10);
|
|
|
|
if ( isNaN(max) === false ) {
|
|
|
|
value = Math.min(value, max);
|
|
|
|
}
|
|
|
|
return value;
|
|
|
|
};
|
|
|
|
|
|
|
|
const toggleOn = function() {
|
|
|
|
const dialog = modalDialog.create(
|
|
|
|
'#loggerSettingsDialog',
|
|
|
|
dialog => {
|
|
|
|
toggleOff(dialog);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// Number inputs
|
|
|
|
let inputs = dialog.querySelectorAll('input[type="number"]');
|
|
|
|
inputs[0].value = settings.discard.maxAge;
|
|
|
|
inputs[1].value = settings.discard.maxLoadCount;
|
|
|
|
inputs[2].value = settings.discard.maxEntryCount;
|
|
|
|
inputs[3].value = settings.linesPerEntry;
|
|
|
|
inputs[3].addEventListener('input', ev => {
|
|
|
|
settings.linesPerEntry = valueFromInput(ev.target, 4);
|
|
|
|
viewPort.updateLayout();
|
|
|
|
});
|
|
|
|
|
|
|
|
// Column checkboxs
|
|
|
|
const onColumnChanged = ev => {
|
|
|
|
const input = ev.target;
|
|
|
|
const i = parseInt(input.getAttribute('data-column'), 10);
|
|
|
|
settings.columns[i] = input.checked !== true;
|
|
|
|
viewPort.updateLayout();
|
|
|
|
};
|
|
|
|
inputs = dialog.querySelectorAll('input[type="checkbox"][data-column]');
|
|
|
|
for ( const input of inputs ) {
|
|
|
|
const i = parseInt(input.getAttribute('data-column'), 10);
|
|
|
|
input.checked = settings.columns[i] === false;
|
|
|
|
input.addEventListener('change', onColumnChanged);
|
|
|
|
}
|
|
|
|
|
|
|
|
modalDialog.show();
|
|
|
|
};
|
|
|
|
|
|
|
|
const toggleOff = function(dialog) {
|
|
|
|
// Number inputs
|
|
|
|
let inputs = dialog.querySelectorAll('input[type="number"]');
|
|
|
|
settings.discard.maxAge = valueFromInput(inputs[0], 240);
|
|
|
|
settings.discard.maxLoadCount = valueFromInput(inputs[1], 25);
|
|
|
|
settings.discard.maxEntryCount = valueFromInput(inputs[2], 2000);
|
|
|
|
settings.linesPerEntry = valueFromInput(inputs[3], 4);
|
|
|
|
|
|
|
|
// Column checkboxs
|
|
|
|
inputs = dialog.querySelectorAll('input[type="checkbox"][data-column]');
|
|
|
|
for ( const input of inputs ) {
|
|
|
|
const i = parseInt(input.getAttribute('data-column'), 10);
|
|
|
|
settings.columns[i] = input.checked !== true;
|
|
|
|
}
|
|
|
|
|
|
|
|
vAPI.localStorage.setItem(
|
|
|
|
'loggerSettings',
|
|
|
|
JSON.stringify(settings)
|
|
|
|
);
|
|
|
|
|
|
|
|
viewPort.updateLayout();
|
|
|
|
};
|
|
|
|
|
2019-05-24 17:18:39 +02:00
|
|
|
uDom.nodeFromId('loggerSettings').addEventListener('click', toggleOn);
|
2018-12-19 22:21:23 +01:00
|
|
|
|
2019-01-12 22:36:20 +01:00
|
|
|
return settings;
|
2015-05-09 00:28:01 +02:00
|
|
|
})();
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2018-12-17 19:54:17 +01:00
|
|
|
logger.resize = (function() {
|
|
|
|
let timer;
|
|
|
|
|
|
|
|
const resize = function() {
|
|
|
|
const vrect = document.body.getBoundingClientRect();
|
|
|
|
const elems = document.querySelectorAll('.vscrollable');
|
|
|
|
for ( const elem of elems ) {
|
|
|
|
const crect = elem.getBoundingClientRect();
|
|
|
|
const dh = crect.bottom - vrect.bottom;
|
|
|
|
if ( dh === 0 ) { continue; }
|
2020-05-05 13:08:38 +02:00
|
|
|
elem.style.height = Math.ceil(crect.height - dh) + 'px';
|
2018-12-17 19:54:17 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const resizeAsync = function() {
|
|
|
|
if ( timer !== undefined ) { return; }
|
|
|
|
timer = self.requestAnimationFrame(( ) => {
|
|
|
|
timer = undefined;
|
|
|
|
resize();
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
resizeAsync();
|
|
|
|
|
|
|
|
window.addEventListener('resize', resizeAsync, { passive: true });
|
|
|
|
|
|
|
|
return resizeAsync;
|
|
|
|
})();
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2018-12-13 18:30:54 +01:00
|
|
|
const grabView = function() {
|
2018-01-09 14:08:17 +01:00
|
|
|
if ( logger.ownerId === undefined ) {
|
|
|
|
logger.ownerId = Date.now();
|
2018-01-08 20:29:39 +01:00
|
|
|
}
|
2018-12-23 21:35:32 +01:00
|
|
|
readLogBuffer();
|
2018-01-08 20:29:39 +01:00
|
|
|
};
|
|
|
|
|
2018-12-13 18:30:54 +01:00
|
|
|
const releaseView = function() {
|
2018-01-09 14:08:17 +01:00
|
|
|
if ( logger.ownerId === undefined ) { return; }
|
2019-09-17 21:15:01 +02:00
|
|
|
vAPI.messaging.send('loggerUI', {
|
|
|
|
what: 'releaseView',
|
|
|
|
ownerId: logger.ownerId,
|
|
|
|
});
|
2018-01-09 14:08:17 +01:00
|
|
|
logger.ownerId = undefined;
|
2018-01-08 20:29:39 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
window.addEventListener('pagehide', releaseView);
|
|
|
|
window.addEventListener('pageshow', grabView);
|
|
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1398625
|
|
|
|
window.addEventListener('beforeunload', releaseView);
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2015-08-01 17:30:54 +02:00
|
|
|
uDom('#pageSelector').on('change', pageSelectorChanged);
|
|
|
|
uDom('#refresh').on('click', reloadTab);
|
2017-11-20 14:42:32 +01:00
|
|
|
uDom('#netInspector .vCompactToggler').on('click', toggleVCompactView);
|
2018-12-17 19:54:17 +01:00
|
|
|
uDom('#pause').on('click', pauseNetInspector);
|
2019-01-12 22:36:20 +01:00
|
|
|
|
2018-05-27 14:31:17 +02:00
|
|
|
// https://github.com/gorhill/uBlock/issues/507
|
2019-01-12 22:36:20 +01:00
|
|
|
// Ensure tab selector is in sync with URL hash
|
2018-05-27 14:31:17 +02:00
|
|
|
pageSelectorFromURLHash();
|
2015-08-01 17:30:54 +02:00
|
|
|
window.addEventListener('hashchange', pageSelectorFromURLHash);
|
2015-05-09 00:28:01 +02:00
|
|
|
|
2018-12-16 21:26:38 +01:00
|
|
|
// Start to watch the current window geometry 2 seconds after the document
|
|
|
|
// is loaded, to be sure no spurious geometry changes will be triggered due
|
|
|
|
// to the window geometry pontentially not settling fast enough.
|
2018-12-14 17:01:21 +01:00
|
|
|
if ( self.location.search.includes('popup=1') ) {
|
2019-01-12 22:36:20 +01:00
|
|
|
window.addEventListener(
|
|
|
|
'load',
|
|
|
|
( ) => {
|
|
|
|
setTimeout(
|
|
|
|
( ) => {
|
|
|
|
popupLoggerBox = {
|
|
|
|
x: self.screenX,
|
|
|
|
y: self.screenY,
|
|
|
|
w: self.outerWidth,
|
|
|
|
h: self.outerHeight,
|
|
|
|
};
|
|
|
|
}, 2000);
|
|
|
|
},
|
|
|
|
{ once: true }
|
|
|
|
);
|
2018-12-14 17:01:21 +01:00
|
|
|
}
|
|
|
|
|
2015-05-09 00:28:01 +02:00
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
})();
|