@browserless.io/browserless 2.12.0-beta-3 → 2.12.0-beta-4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/build/browsers/chromium.cdp.d.ts +1 -1
  2. package/build/browsers/chromium.cdp.js +2 -2
  3. package/build/browsers/chromium.playwright.d.ts +1 -1
  4. package/build/browsers/chromium.playwright.js +2 -2
  5. package/build/browsers/firefox.playwright.d.ts +1 -1
  6. package/build/browsers/firefox.playwright.js +2 -2
  7. package/build/browsers/index.d.ts +3 -2
  8. package/build/browsers/index.js +36 -17
  9. package/build/browsers/webkit.playwright.d.ts +1 -1
  10. package/build/browsers/webkit.playwright.js +4 -4
  11. package/build/routes/chrome/http/pdf.post.body.json +8 -8
  12. package/build/routes/chrome/http/scrape.post.body.json +8 -8
  13. package/build/routes/chrome/http/screenshot.post.body.json +8 -8
  14. package/build/routes/chromium/http/content.post.body.json +8 -8
  15. package/build/routes/chromium/http/pdf.post.body.json +8 -8
  16. package/build/routes/chromium/http/scrape.post.body.json +8 -8
  17. package/build/types.d.ts +1 -1
  18. package/extensions/ublock/_locales/eu/messages.json +4 -4
  19. package/extensions/ublock/_locales/hi/messages.json +5 -5
  20. package/extensions/ublock/_locales/kn/messages.json +11 -11
  21. package/extensions/ublock/_locales/nb/messages.json +2 -2
  22. package/extensions/ublock/_locales/no/messages.json +2 -2
  23. package/extensions/ublock/_locales/ro/messages.json +1 -1
  24. package/extensions/ublock/_locales/sv/messages.json +1 -1
  25. package/extensions/ublock/_locales/zh_CN/messages.json +2 -2
  26. package/extensions/ublock/assets/assets.json +3 -8
  27. package/extensions/ublock/assets/resources/scriptlets.js +128 -31
  28. package/extensions/ublock/assets/thirdparties/easylist/easylist.txt +4870 -3560
  29. package/extensions/ublock/assets/thirdparties/easylist/easyprivacy.txt +662 -173
  30. package/extensions/ublock/assets/thirdparties/pgl.yoyo.org/as/serverlist +10 -42
  31. package/extensions/ublock/assets/thirdparties/publicsuffix.org/list/effective_tld_names.dat +241 -80
  32. package/extensions/ublock/assets/thirdparties/urlhaus-filter/urlhaus-filter-online.txt +2093 -1224
  33. package/extensions/ublock/assets/ublock/badlists.txt +2 -0
  34. package/extensions/ublock/assets/ublock/badware.min.txt +408 -287
  35. package/extensions/ublock/assets/ublock/filters.min.txt +947 -645
  36. package/extensions/ublock/assets/ublock/privacy.min.txt +43 -8
  37. package/extensions/ublock/assets/ublock/quick-fixes.min.txt +55 -93
  38. package/extensions/ublock/assets/ublock/unbreak.min.txt +52 -19
  39. package/extensions/ublock/css/1p-filters.css +2 -0
  40. package/extensions/ublock/css/codemirror.css +2 -2
  41. package/extensions/ublock/css/dashboard.css +2 -5
  42. package/extensions/ublock/css/epicker-ui.css +3 -3
  43. package/extensions/ublock/css/fa-icons.css +3 -0
  44. package/extensions/ublock/css/logger-ui-inspector.css +1 -0
  45. package/extensions/ublock/css/logger-ui.css +44 -32
  46. package/extensions/ublock/img/fontawesome/fontawesome-defs.svg +1 -0
  47. package/extensions/ublock/js/3p-filters.js +4 -5
  48. package/extensions/ublock/js/biditrie.js +16 -11
  49. package/extensions/ublock/js/cachestorage.js +37 -37
  50. package/extensions/ublock/js/contentscript-extra.js +0 -2
  51. package/extensions/ublock/js/contentscript.js +1 -6
  52. package/extensions/ublock/js/epicker-ui.js +28 -36
  53. package/extensions/ublock/js/fa-icons.js +1 -0
  54. package/extensions/ublock/js/hntrie.js +19 -13
  55. package/extensions/ublock/js/logger-ui-inspector.js +6 -13
  56. package/extensions/ublock/js/logger-ui.js +264 -264
  57. package/extensions/ublock/js/s14e-serializer.js +267 -264
  58. package/extensions/ublock/js/scriptlet-filtering.js +12 -18
  59. package/extensions/ublock/js/scriptlets/dom-inspector.js +1 -5
  60. package/extensions/ublock/js/scriptlets/epicker.js +53 -59
  61. package/extensions/ublock/js/start.js +0 -8
  62. package/extensions/ublock/js/storage.js +2 -9
  63. package/extensions/ublock/js/vapi-background.js +19 -20
  64. package/extensions/ublock/js/vapi-common.js +2 -7
  65. package/extensions/ublock/js/vapi.js +0 -4
  66. package/extensions/ublock/js/webext.js +23 -15
  67. package/extensions/ublock/logger-ui.html +24 -15
  68. package/extensions/ublock/manifest.json +2 -3
  69. package/package.json +2 -2
  70. package/src/browsers/chromium.cdp.ts +2 -2
  71. package/src/browsers/chromium.playwright.ts +2 -2
  72. package/src/browsers/firefox.playwright.ts +2 -3
  73. package/src/browsers/index.ts +49 -21
  74. package/src/browsers/webkit.playwright.ts +4 -4
  75. package/src/routes/chrome/tests/websocket.spec.ts +2 -2
  76. package/src/routes/chromium/tests/websocket.spec.ts +2 -2
  77. package/src/routes/firefox/tests/websocket.spec.ts +2 -4
  78. package/src/routes/webkit/tests/websocket.spec.ts +2 -3
  79. package/src/types.ts +1 -1
  80. package/src/utils.ts +1 -1
  81. package/static/docs/swagger.json +1 -1
  82. package/static/docs/swagger.min.json +1 -1
@@ -19,12 +19,10 @@
19
19
  Home: https://github.com/gorhill/uBlock
20
20
  */
21
21
 
22
- 'use strict';
23
-
22
+ import { dom, qs$, qsa$ } from './dom.js';
23
+ import { i18n, i18n$ } from './i18n.js';
24
24
  import { broadcast } from './broadcast.js';
25
25
  import { hostnameFromURI } from './uri-utils.js';
26
- import { i18n, i18n$ } from './i18n.js';
27
- import { dom, qs$, qsa$ } from './dom.js';
28
26
 
29
27
  /******************************************************************************/
30
28
 
@@ -36,7 +34,6 @@ const logger = self.logger = { ownerId: Date.now() };
36
34
  const logDate = new Date();
37
35
  const logDateTimezoneOffset = logDate.getTimezoneOffset() * 60;
38
36
  const loggerEntries = [];
39
- let loggerEntryIdGenerator = 1;
40
37
 
41
38
  const COLUMN_TIMESTAMP = 0;
42
39
  const COLUMN_FILTER = 1;
@@ -73,95 +70,14 @@ const tabIdFromAttribute = function(elem) {
73
70
  return isNaN(tabId) ? 0 : tabId;
74
71
  };
75
72
 
73
+ const hasOwnProperty = (o, p) =>
74
+ Object.prototype.hasOwnProperty.call(o, p);
76
75
 
77
- /******************************************************************************/
78
- /******************************************************************************/
79
-
80
- const onStartMovingWidget = (( ) => {
81
- let widget = null;
82
- let ondone = null;
83
- let mx0 = 0, my0 = 0;
84
- let mx1 = 0, my1 = 0;
85
- let l0 = 0, t0 = 0;
86
- let pw = 0, ph = 0;
87
- let cw = 0, ch = 0;
88
- let timer;
89
-
90
- const xyFromEvent = ev => {
91
- if ( ev.type.startsWith('mouse') ) {
92
- return { x: ev.pageX, y: ev.pageY };
93
- }
94
- const touch = ev.touches[0];
95
- return { x: touch.pageX, y: touch.pageY };
96
- };
97
-
98
- const eatEvent = function(ev) {
99
- ev.stopPropagation();
100
- if ( ev.touches !== undefined ) { return; }
101
- ev.preventDefault();
102
- };
76
+ const dispatchTabidChange = vAPI.defer.create(( ) => {
77
+ document.dispatchEvent(new Event('tabIdChanged'));
78
+ });
103
79
 
104
- const move = ( ) => {
105
- timer = undefined;
106
- const l1 = Math.min(Math.max(l0 + mx1 - mx0, 0), Math.max(pw - cw, 0));
107
- if ( (l1+cw/2) < (pw/2) ) {
108
- widget.style.left = `${l1/pw*100}%`;
109
- widget.style.right = '';
110
- } else {
111
- widget.style.right = `${(pw-l1-cw)/pw*100}%`;
112
- widget.style.left = '';
113
- }
114
- const t1 = Math.min(Math.max(t0 + my1 - my0, 0), Math.max(ph - ch, 0));
115
- widget.style.top = `${t1/ph*100}%`;
116
- widget.style.bottom = '';
117
- };
118
-
119
- const moveAsync = ev => {
120
- if ( timer !== undefined ) { return; }
121
- const coord = xyFromEvent(ev);
122
- mx1 = coord.x; my1 = coord.y;
123
- timer = self.requestAnimationFrame(move);
124
- eatEvent(ev);
125
- };
126
-
127
- const stop = ev => {
128
- if ( timer !== undefined ) {
129
- self.cancelAnimationFrame(timer);
130
- timer = undefined;
131
- }
132
- if ( widget === null ) { return; }
133
- if ( widget.classList.contains('moving') === false ) { return; }
134
- widget.classList.remove('moving');
135
- self.removeEventListener('mousemove', moveAsync, { capture: true });
136
- self.removeEventListener('touchmove', moveAsync, { capture: true });
137
- eatEvent(ev);
138
- widget = null;
139
- if ( ondone !== null ) {
140
- ondone();
141
- ondone = null;
142
- }
143
- };
144
-
145
- return function(ev, target, callback) {
146
- if ( dom.cl.has(target, 'moving') ) { return; }
147
- widget = target;
148
- ondone = callback || null;
149
- const coord = xyFromEvent(ev);
150
- mx0 = coord.x; my0 = coord.y;
151
- const widgetParent = widget.parentElement;
152
- const crect = widget.getBoundingClientRect();
153
- const prect = widgetParent.getBoundingClientRect();
154
- pw = prect.width; ph = prect.height;
155
- cw = crect.width; ch = crect.height;
156
- l0 = crect.x - prect.x; t0 = crect.y - prect.y;
157
- widget.classList.add('moving');
158
- self.addEventListener('mousemove', moveAsync, { capture: true });
159
- self.addEventListener('mouseup', stop, { capture: true, once: true });
160
- self.addEventListener('touchmove', moveAsync, { capture: true });
161
- self.addEventListener('touchend', stop, { capture: true, once: true });
162
- eatEvent(ev);
163
- };
164
- })();
80
+ const escapeRegexStr = s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
165
81
 
166
82
  /******************************************************************************/
167
83
  /******************************************************************************/
@@ -247,7 +163,7 @@ const regexFromURLFilteringResult = function(result) {
247
163
  if ( url === '*' ) {
248
164
  return new RegExp('^.*$', 'gi');
249
165
  }
250
- return new RegExp('^' + url.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
166
+ return new RegExp('^' + escapeRegexStr(url), 'gi');
251
167
  };
252
168
 
253
169
  /******************************************************************************/
@@ -260,7 +176,7 @@ const nodeFromURL = function(parent, url, re, type) {
260
176
  fragment.textContent = url;
261
177
  } else {
262
178
  if ( typeof re === 'string' ) {
263
- re = new RegExp(re.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g');
179
+ re = new RegExp(escapeRegexStr(re), 'g');
264
180
  }
265
181
  const matches = re.exec(url);
266
182
  if ( matches === null || matches[0].length === 0 ) {
@@ -285,17 +201,17 @@ const nodeFromURL = function(parent, url, re, type) {
285
201
  const a = document.createElement('a');
286
202
  let href = url;
287
203
  switch ( type ) {
288
- case 'css':
289
- case 'doc':
290
- case 'frame':
291
- case 'object':
292
- case 'other':
293
- case 'script':
294
- case 'xhr':
295
- href = `code-viewer.html?url=${encodeURIComponent(href)}`;
296
- break;
297
- default:
298
- break;
204
+ case 'css':
205
+ case 'doc':
206
+ case 'frame':
207
+ case 'object':
208
+ case 'other':
209
+ case 'script':
210
+ case 'xhr':
211
+ href = `code-viewer.html?url=${encodeURIComponent(href)}`;
212
+ break;
213
+ default:
214
+ break;
299
215
  }
300
216
  dom.attr(a, 'href', href);
301
217
  dom.attr(a, 'target', '_blank');
@@ -316,45 +232,44 @@ const normalizeToStr = function(s) {
316
232
 
317
233
  /******************************************************************************/
318
234
 
319
- const LogEntry = function(details) {
320
- if ( details instanceof Object === false ) { return; }
321
- const receiver = LogEntry.prototype;
322
- for ( const prop in receiver ) {
323
- if ( details.hasOwnProperty(prop) === false ) { continue; }
324
- if ( details[prop] === receiver[prop] ) { continue; }
325
- this[prop] = details[prop];
326
- }
327
- this.id = `${loggerEntryIdGenerator++}`;
328
- if ( details.aliasURL !== undefined ) {
329
- this.aliased = true;
330
- }
331
- if ( this.tabDomain === '' ) {
332
- this.tabDomain = this.tabHostname || '';
333
- }
334
- if ( this.docDomain === '' ) {
335
- this.docDomain = this.docHostname || '';
336
- }
337
- if ( this.domain === '' ) {
338
- this.domain = details.hostname || '';
235
+ class LogEntry {
236
+ static IdGenerator = 1;
237
+ constructor(details) {
238
+ this.aliased = false;
239
+ this.dead = false;
240
+ this.docDomain = '';
241
+ this.docHostname = '';
242
+ this.domain = '';
243
+ this.filter = undefined;
244
+ this.id = LogEntry.IdGenerator++;
245
+ this.method = '';
246
+ this.realm = '';
247
+ this.tabDomain = '';
248
+ this.tabHostname = '';
249
+ this.tabId = undefined;
250
+ this.textContent = '';
251
+ this.tstamp = 0;
252
+ this.type = '';
253
+ this.voided = false;
254
+ if ( details instanceof Object === false ) { return; }
255
+ for ( const prop in this ) {
256
+ if ( hasOwnProperty(details, prop) === false ) { continue; }
257
+ this[prop] = details[prop];
258
+ }
259
+ if ( details.aliasURL !== undefined ) {
260
+ this.aliased = true;
261
+ }
262
+ if ( this.tabDomain === '' ) {
263
+ this.tabDomain = this.tabHostname || '';
264
+ }
265
+ if ( this.docDomain === '' ) {
266
+ this.docDomain = this.docHostname || '';
267
+ }
268
+ if ( this.domain === '' ) {
269
+ this.domain = details.hostname || '';
270
+ }
339
271
  }
340
- };
341
- LogEntry.prototype = {
342
- aliased: false,
343
- dead: false,
344
- docDomain: '',
345
- docHostname: '',
346
- domain: '',
347
- filter: undefined,
348
- method: '',
349
- realm: '',
350
- tabDomain: '',
351
- tabHostname: '',
352
- tabId: undefined,
353
- textContent: '',
354
- tstamp: 0,
355
- type: '',
356
- voided: false,
357
- };
272
+ }
358
273
 
359
274
  /******************************************************************************/
360
275
 
@@ -442,10 +357,10 @@ const processLoggerEntries = function(response) {
442
357
  }
443
358
 
444
359
  const addedCount = filteredLoggerEntries.length - previousCount;
445
- if ( addedCount !== 0 ) {
446
- viewPort.updateContent(addedCount);
447
- rowJanitor.inserted(addedCount);
448
- }
360
+ if ( addedCount === 0 ) { return; }
361
+ viewPort.updateContent(addedCount);
362
+ rowJanitor.inserted(addedCount);
363
+ consolePane.updateContent();
449
364
  };
450
365
 
451
366
  /******************************************************************************/
@@ -716,10 +631,11 @@ const viewPort = (( ) => {
716
631
  };
717
632
 
718
633
  const resizeTimer = vAPI.defer.create(onLayoutChanged);
719
- const updateLayout = function() {
634
+ const updateLayout = ( ) => {
720
635
  resizeTimer.onvsync(1000/8);
721
636
  };
722
- dom.on(window, 'resize', updateLayout, { passive: true });
637
+ const resizeObserver = new self.ResizeObserver(updateLayout);
638
+ resizeObserver.observe(qs$('#netInspector .vscrollable'));
723
639
 
724
640
  updateLayout();
725
641
 
@@ -870,7 +786,7 @@ const viewPort = (( ) => {
870
786
  if ( cells.length > 8 ) {
871
787
  const pos = details.textContent.lastIndexOf('\x1FaliasURL=');
872
788
  if ( pos !== -1 ) {
873
- dom.attr(div, 'data-aliasid', details.id);
789
+ div.dataset.aliasid = `${details.id}`;
874
790
  }
875
791
  }
876
792
 
@@ -969,7 +885,7 @@ const viewPort = (( ) => {
969
885
  vwScroller.scrollTop = lastTopPix;
970
886
  };
971
887
 
972
- return { updateContent, updateLayout, };
888
+ return { updateContent, updateLayout };
973
889
  })();
974
890
 
975
891
  /******************************************************************************/
@@ -1008,7 +924,7 @@ const synchronizeTabIds = function(newTabIds) {
1008
924
  // Mark as "void" all logger entries which are linked to now invalid
1009
925
  // tab ids.
1010
926
  // When an entry is voided without being removed, we re-create a new entry
1011
- // in order to ensure the entry has a new identity. A new identify ensures
927
+ // in order to ensure the entry has a new identity. A new identity ensures
1012
928
  // that identity-based associations elsewhere are automatically
1013
929
  // invalidated.
1014
930
  if ( toVoid.size !== 0 ) {
@@ -1206,11 +1122,11 @@ const pageSelectorFromURLHash = (( ) => {
1206
1122
  if ( lastSelectedTabId === selectedTabId ) { return; }
1207
1123
 
1208
1124
  rowFilterer.filterAll();
1209
- document.dispatchEvent(new Event('tabIdChanged'));
1210
1125
  updateCurrentTabTitle();
1211
1126
  dom.cl.toggle('.needdom', 'disabled', selectedTabId <= 0);
1212
1127
  dom.cl.toggle('.needscope', 'disabled', selectedTabId <= 0);
1213
1128
  lastSelectedTabId = selectedTabId;
1129
+ dispatchTabidChange.onric({ timeout: 1000 });
1214
1130
  };
1215
1131
  })();
1216
1132
 
@@ -1234,18 +1150,18 @@ dom.on(document, 'keydown', ev => {
1234
1150
  if ( ev.isComposing ) { return; }
1235
1151
  let bypassCache = false;
1236
1152
  switch ( ev.key ) {
1237
- case 'F5':
1238
- bypassCache = ev.ctrlKey || ev.metaKey || ev.shiftKey;
1239
- break;
1240
- case 'r':
1241
- if ( (ev.ctrlKey || ev.metaKey) !== true ) { return; }
1242
- break;
1243
- case 'R':
1244
- if ( (ev.ctrlKey || ev.metaKey) !== true ) { return; }
1245
- bypassCache = true;
1246
- break;
1247
- default:
1248
- return;
1153
+ case 'F5':
1154
+ bypassCache = ev.ctrlKey || ev.metaKey || ev.shiftKey;
1155
+ break;
1156
+ case 'r':
1157
+ if ( (ev.ctrlKey || ev.metaKey) !== true ) { return; }
1158
+ break;
1159
+ case 'R':
1160
+ if ( (ev.ctrlKey || ev.metaKey) !== true ) { return; }
1161
+ bypassCache = true;
1162
+ break;
1163
+ default:
1164
+ return;
1249
1165
  }
1250
1166
  reloadTab(bypassCache);
1251
1167
  ev.preventDefault();
@@ -1256,7 +1172,7 @@ dom.on(document, 'keydown', ev => {
1256
1172
  /******************************************************************************/
1257
1173
 
1258
1174
  (( ) => {
1259
- const reRFC3986 = /^([^:\/?#]+:)?(\/\/[^\/?#]*)?([^?#]*)(\?[^#]*)?(#.*)?/;
1175
+ const reRFC3986 = /^([^:/?#]+:)?(\/\/[^/?#]*)?([^?#]*)(\?[^#]*)?(#.*)?/;
1260
1176
  const reSchemeOnly = /^[\w-]+:$/;
1261
1177
  const staticFilterTypes = {
1262
1178
  'beacon': 'ping',
@@ -1308,7 +1224,7 @@ dom.on(document, 'keydown', ev => {
1308
1224
  const onColorsReady = function(response) {
1309
1225
  dom.cl.toggle(dom.body, 'dirty', response.dirty);
1310
1226
  for ( const url in response.colors ) {
1311
- if ( response.colors.hasOwnProperty(url) === false ) { continue; }
1227
+ if ( hasOwnProperty(response.colors, url) === false ) { continue; }
1312
1228
  const colorEntry = response.colors[url];
1313
1229
  const node = qs$(dialog, `.dynamic .entry .action[data-url="${url}"]`);
1314
1230
  if ( node === null ) { continue; }
@@ -1375,7 +1291,7 @@ dom.on(document, 'keydown', ev => {
1375
1291
  dom.cl.toggle(
1376
1292
  qs$(dialog, '#createStaticFilter'),
1377
1293
  'disabled',
1378
- createdStaticFilters.hasOwnProperty(value) || value === ''
1294
+ hasOwnProperty(createdStaticFilters, value) || value === ''
1379
1295
  );
1380
1296
  };
1381
1297
 
@@ -1416,7 +1332,7 @@ dom.on(document, 'keydown', ev => {
1416
1332
  const value = staticFilterNode().value
1417
1333
  .replace(/^((?:@@)?\/.+\/)(\$|$)/, '$1*$2');
1418
1334
  // Avoid duplicates
1419
- if ( createdStaticFilters.hasOwnProperty(value) ) { return; }
1335
+ if ( hasOwnProperty(createdStaticFilters, value) ) { return; }
1420
1336
  createdStaticFilters[value] = true;
1421
1337
  // https://github.com/uBlockOrigin/uBlock-issues/issues/1281#issuecomment-704217175
1422
1338
  // TODO:
@@ -1624,7 +1540,7 @@ dom.on(document, 'keydown', ev => {
1624
1540
  const aliasURLFromID = function(id) {
1625
1541
  if ( id === '' ) { return ''; }
1626
1542
  for ( const entry of loggerEntries ) {
1627
- if ( entry.id !== id ) { continue; }
1543
+ if ( `${entry.id}` !== id ) { continue; }
1628
1544
  const match = /\baliasURL=([^\x1F]+)/.exec(entry.textContent);
1629
1545
  if ( match === null ) { return ''; }
1630
1546
  return match[1];
@@ -1796,7 +1712,7 @@ dom.on(document, 'keydown', ev => {
1796
1712
  rows[7].style.display = 'none';
1797
1713
  }
1798
1714
  // Alias URL
1799
- text = dom.attr(tr, 'data-aliasid');
1715
+ text = tr.dataset.aliasid;
1800
1716
  const aliasURL = text ? aliasURLFromID(text) : '';
1801
1717
  if ( aliasURL !== '' ) {
1802
1718
  rows[8].children[1].textContent =
@@ -1965,22 +1881,6 @@ dom.on(document, 'keydown', ev => {
1965
1881
  parseStaticInputs();
1966
1882
  };
1967
1883
 
1968
- const moveDialog = ev => {
1969
- if ( ev.button !== 0 && ev.touches === undefined ) { return; }
1970
- const widget = qs$('#netInspector .entryTools');
1971
- onStartMovingWidget(ev, widget, ( ) => {
1972
- vAPI.localStorage.setItem(
1973
- 'loggerUI.entryTools',
1974
- JSON.stringify({
1975
- bottom: widget.style.bottom,
1976
- left: widget.style.left,
1977
- right: widget.style.right,
1978
- top: widget.style.top,
1979
- })
1980
- );
1981
- });
1982
- };
1983
-
1984
1884
  const fillDialog = function(domains) {
1985
1885
  dialog = dom.clone('#templates .netFilteringDialog');
1986
1886
  dom.cl.toggle(
@@ -1998,15 +1898,12 @@ dom.on(document, 'keydown', ev => {
1998
1898
  dom.on(dialog, 'click', ev => { onClick(ev); }, true);
1999
1899
  dom.on(dialog, 'change', onSelectChange, true);
2000
1900
  dom.on(dialog, 'input', onInputChange, true);
2001
- const container = qs$('#netInspector .entryTools');
1901
+ const container = qs$('#inspectors .entryTools');
2002
1902
  if ( container.firstChild ) {
2003
1903
  container.replaceChild(dialog, container.firstChild);
2004
1904
  } else {
2005
1905
  container.append(dialog);
2006
1906
  }
2007
- const moveBand = qs$(dialog, '.moveBand');
2008
- dom.on(moveBand, 'mousedown', moveDialog);
2009
- dom.on(moveBand, 'touchstart', moveDialog);
2010
1907
  };
2011
1908
 
2012
1909
  const toggleOn = async function(ev) {
@@ -2036,7 +1933,7 @@ dom.on(document, 'keydown', ev => {
2036
1933
  };
2037
1934
 
2038
1935
  const toggleOff = function() {
2039
- const container = qs$('#netInspector .entryTools');
1936
+ const container = qs$('#inspectors .entryTools');
2040
1937
  if ( container.firstChild ) {
2041
1938
  container.firstChild.remove();
2042
1939
  }
@@ -2046,32 +1943,19 @@ dom.on(document, 'keydown', ev => {
2046
1943
  };
2047
1944
 
2048
1945
  // Restore position of entry tools dialog
2049
- vAPI.localStorage.getItemAsync(
2050
- 'loggerUI.entryTools',
2051
- ).then(response => {
2052
- if ( typeof response !== 'string' ) { return; }
2053
- const settings = JSON.parse(response);
2054
- const widget = qs$('#netInspector .entryTools');
2055
- widget.style.bottom = '';
2056
- widget.style.left = settings.left || '';
2057
- widget.style.right = settings.right || '';
2058
- widget.style.top = settings.top || '';
2059
- if ( /^-/.test(widget.style.top) ) {
2060
- widget.style.top = '0';
2061
- }
2062
- });
1946
+ vAPI.localStorage.removeItem('loggerUI.entryTools');
2063
1947
 
2064
1948
  // This is to detect text selection, in which case the click won't be
2065
1949
  // interpreted as a request to open the details of the entry.
2066
1950
  let selectionAtMouseDown;
2067
1951
  let selectionAtTimer;
2068
- dom.on('#netInspector', 'mousedown', '.canDetails *', ev => {
1952
+ dom.on('#netInspector', 'mousedown', '.canDetails *:not(a)', ev => {
2069
1953
  if ( ev.button !== 0 ) { return; }
2070
1954
  if ( selectionAtMouseDown !== undefined ) { return; }
2071
1955
  selectionAtMouseDown = document.getSelection().toString();
2072
1956
  });
2073
1957
 
2074
- dom.on('#netInspector', 'click', '.canDetails *', ev => {
1958
+ dom.on('#netInspector', 'click', '.canDetails *:not(a)', ev => {
2075
1959
  if ( ev.button !== 0 ) { return; }
2076
1960
  if ( selectionAtTimer !== undefined ) {
2077
1961
  clearTimeout(selectionAtTimer);
@@ -2086,27 +1970,165 @@ dom.on(document, 'keydown', ev => {
2086
1970
  }, 333);
2087
1971
  });
2088
1972
 
2089
- dom.on(
2090
- '#netInspector',
2091
- 'click',
2092
- '.logEntry > div > span:nth-of-type(8) a',
2093
- ev => {
2094
- vAPI.messaging.send('codeViewer', {
2095
- what: 'gotoURL',
2096
- details: {
2097
- url: ev.target.getAttribute('href'),
2098
- select: true,
2099
- },
2100
- });
2101
- ev.preventDefault();
2102
- ev.stopPropagation();
2103
- }
1973
+ dom.on('#netInspector', 'click', '.logEntry > div > span:nth-of-type(8) a', ev => {
1974
+ vAPI.messaging.send('codeViewer', {
1975
+ what: 'gotoURL',
1976
+ details: {
1977
+ url: ev.target.getAttribute('href'),
1978
+ select: true,
1979
+ },
1980
+ });
1981
+ ev.preventDefault();
1982
+ ev.stopPropagation();
1983
+ }
2104
1984
  );
2105
1985
  })();
2106
1986
 
2107
1987
  /******************************************************************************/
2108
1988
  /******************************************************************************/
2109
1989
 
1990
+ const consolePane = (( ) => {
1991
+ let on = false;
1992
+
1993
+ const lastInfoEntry = ( ) => {
1994
+ let j = Number.MAX_SAFE_INTEGER;
1995
+ let i = loggerEntries.length;
1996
+ while ( i-- ) {
1997
+ const entry = loggerEntries[i];
1998
+ if ( entry.tabId !== selectedTabId ) { continue; }
1999
+ if ( entry.realm !== 'message' ) { continue; }
2000
+ if ( entry.voided ) { continue; }
2001
+ j = entry.id;
2002
+ }
2003
+ return j;
2004
+ };
2005
+
2006
+ const filterExpr = {
2007
+ not: true,
2008
+ pattern: '',
2009
+ };
2010
+
2011
+ const filterExprFromInput = ( ) => {
2012
+ const raw = qs$('#infoInspector .permatoolbar input').value.trim();
2013
+ if ( raw.startsWith('-') && raw.length > 1 ) {
2014
+ filterExpr.pattern = raw.slice(1);
2015
+ filterExpr.not = true;
2016
+ } else {
2017
+ filterExpr.pattern = raw;
2018
+ filterExpr.not = false;
2019
+ }
2020
+ if ( filterExpr.pattern !== '' ) {
2021
+ filterExpr.pattern = new RegExp(escapeRegexStr(filterExpr.pattern), 'i');
2022
+ }
2023
+ };
2024
+
2025
+ const addRows = ( ) => {
2026
+ const { not, pattern } = filterExpr;
2027
+ const topRow = qs$('#infoInspector .vscrollable > div');
2028
+ const topid = topRow !== null ? parseInt(topRow.dataset.id, 10) : 0;
2029
+ const fragment = new DocumentFragment();
2030
+ for ( const entry of loggerEntries ) {
2031
+ if ( entry.id <= topid ) { break; }
2032
+ if ( entry.tabId !== selectedTabId ) { continue; }
2033
+ if ( entry.realm !== 'message' ) { continue; }
2034
+ if ( entry.voided ) { continue; }
2035
+ const fields = entry.textContent.split('\x1F').slice(0, 2);
2036
+ const textContent = fields.join('\xA0');
2037
+ if ( pattern instanceof RegExp ) {
2038
+ if ( pattern.test(textContent) === not ) { continue; }
2039
+ }
2040
+ const div = document.createElement('div');
2041
+ div.dataset.id = `${entry.id}`;
2042
+ div.dataset.type = entry.type;
2043
+ div.textContent = textContent;
2044
+ fragment.append(div);
2045
+ }
2046
+ const container = qs$('#infoInspector .vscrollable');
2047
+ container.prepend(fragment);
2048
+ }
2049
+
2050
+ const removeRows = (before = 0) => {
2051
+ if ( before === 0 ) {
2052
+ before = lastInfoEntry();
2053
+ }
2054
+ const rows = qsa$('#infoInspector .vscrollable > div');
2055
+ let i = rows.length;
2056
+ while ( i-- ) {
2057
+ const div = rows[i];
2058
+ const id = parseInt(div.dataset.id, 10);
2059
+ if ( id > before ) { break; }
2060
+ div.remove();
2061
+ }
2062
+ }
2063
+
2064
+ const updateContent = ( ) => {
2065
+ if ( on === false ) { return; }
2066
+ removeRows();
2067
+ addRows();
2068
+ };
2069
+
2070
+ const onTabIdChanged = ( ) => {
2071
+ if ( on === false ) { return; }
2072
+ removeRows(Number.MAX_SAFE_INTEGER);
2073
+ addRows();
2074
+ };
2075
+
2076
+ const toggleOn = ( ) => {
2077
+ if ( on ) { return; }
2078
+ addRows();
2079
+ dom.on(document, 'tabIdChanged', onTabIdChanged);
2080
+ on = true;
2081
+ };
2082
+
2083
+ const toggleOff = ( ) => {
2084
+ removeRows(Number.MAX_SAFE_INTEGER);
2085
+ dom.off(document, 'tabIdChanged', onTabIdChanged);
2086
+ on = false;
2087
+ };
2088
+
2089
+ const resizeObserver = new self.ResizeObserver(entries => {
2090
+ if ( entries.length === 0 ) { return; }
2091
+ const rect = entries[0].contentRect;
2092
+ if ( rect.width > 0 && rect.height > 0 ) {
2093
+ toggleOn();
2094
+ } else {
2095
+ toggleOff();
2096
+ }
2097
+ });
2098
+ resizeObserver.observe(qs$('#infoInspector'));
2099
+
2100
+ dom.on('button.logConsole', 'click', ev => {
2101
+ const active = dom.cl.toggle('#inspectors', 'console');
2102
+ dom.cl.toggle(ev.currentTarget, 'active', active);
2103
+ });
2104
+
2105
+ dom.on('#infoInspector button#clearConsole', 'click', ( ) => {
2106
+ const ids = [];
2107
+ qsa$('#infoInspector .vscrollable > div').forEach(div => {
2108
+ ids.push(parseInt(div.dataset.id, 10));
2109
+ });
2110
+ rowJanitor.removeSpecificRows(ids);
2111
+ });
2112
+
2113
+ dom.on('#infoInspector button#logLevel', 'click', ev => {
2114
+ const level = dom.cl.toggle(ev.currentTarget, 'active') ? 2 : 1;
2115
+ broadcast({ what: 'loggerLevelChanged', level });
2116
+ });
2117
+
2118
+ const throttleFilter = vAPI.defer.create(( ) => {
2119
+ filterExprFromInput();
2120
+ updateContent();
2121
+ });
2122
+ dom.on('#infoInspector .permatoolbar input', 'input', ( ) => {
2123
+ throttleFilter.offon(517);
2124
+ });
2125
+
2126
+ return { updateContent };
2127
+ })();
2128
+
2129
+ /******************************************************************************/
2130
+ /******************************************************************************/
2131
+
2110
2132
  const rowFilterer = (( ) => {
2111
2133
  const userFilters = [];
2112
2134
  const builtinFilters = [];
@@ -2148,7 +2170,7 @@ const rowFilterer = (( ) => {
2148
2170
  rawPart = rawPart.slice(0, -1);
2149
2171
  }
2150
2172
  // https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions
2151
- reStr = rawPart.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
2173
+ reStr = escapeRegexStr(rawPart);
2152
2174
  // https://github.com/orgs/uBlockOrigin/teams/ublock-issues-volunteers/discussions/51
2153
2175
  // Be more flexible when interpreting leading/trailing pipes,
2154
2176
  // as leading/trailing pipes are often used in static filters.
@@ -2415,6 +2437,7 @@ const rowJanitor = (( ) => {
2415
2437
  if ( modified === false ) { return; }
2416
2438
 
2417
2439
  rowFilterer.filterAll();
2440
+ consolePane.updateContent();
2418
2441
  };
2419
2442
 
2420
2443
  const discardAsync = function(deadline) {
@@ -2488,11 +2511,25 @@ const rowJanitor = (( ) => {
2488
2511
  dom.on('#clear', 'click', clear);
2489
2512
 
2490
2513
  return {
2491
- inserted: function(count) {
2514
+ inserted(count) {
2492
2515
  if ( rowIndex !== 0 ) {
2493
2516
  rowIndex += count;
2494
2517
  }
2495
2518
  },
2519
+ removeSpecificRows(descendingIds) {
2520
+ if ( descendingIds.length === 0 ) { return; }
2521
+ let i = loggerEntries.length;
2522
+ let id = descendingIds.pop();
2523
+ while ( i-- ) {
2524
+ const entry = loggerEntries[i];
2525
+ if ( entry.id !== id ) { continue; }
2526
+ loggerEntries.splice(i, 1);
2527
+ if ( descendingIds.length === 0 ) { break; }
2528
+ id = descendingIds.pop();
2529
+ }
2530
+ rowFilterer.filterAll();
2531
+ consolePane.updateContent();
2532
+ },
2496
2533
  };
2497
2534
  })();
2498
2535
 
@@ -2704,7 +2741,6 @@ const loggerStats = (( ) => {
2704
2741
  for ( const entry of filteredLoggerEntries ) {
2705
2742
  const text = entry.textContent;
2706
2743
  const fields = [];
2707
- let i = 0;
2708
2744
  let beg = text.indexOf('\x1F');
2709
2745
  if ( beg === 0 ) { continue; }
2710
2746
  let timeField = text.slice(0, beg);
@@ -2718,7 +2754,6 @@ const loggerStats = (( ) => {
2718
2754
  if ( end === -1 ) { end = text.length; }
2719
2755
  fields.push(text.slice(beg, end));
2720
2756
  beg = end + 1;
2721
- i += 1;
2722
2757
  }
2723
2758
  lines.push(fields);
2724
2759
  }
@@ -2800,7 +2835,7 @@ const loggerStats = (( ) => {
2800
2835
  };
2801
2836
 
2802
2837
  const setRadioButton = function(group, value) {
2803
- if ( options.hasOwnProperty(group) === false ) { return; }
2838
+ if ( hasOwnProperty(options, group) === false ) { return; }
2804
2839
  const groupEl = qs$(dialog, `[data-radio="${group}"]`);
2805
2840
  const buttonEls = qsa$(groupEl, '[data-radio-item]');
2806
2841
  for ( const buttonEl of buttonEls ) {
@@ -2902,7 +2937,7 @@ const loggerSettings = (( ) => {
2902
2937
  if ( Array.isArray(stored.columns) ) {
2903
2938
  settings.columns = stored.columns;
2904
2939
  }
2905
- } catch(ex) {
2940
+ } catch(_) {
2906
2941
  }
2907
2942
  });
2908
2943
 
@@ -2986,36 +3021,6 @@ const loggerSettings = (( ) => {
2986
3021
 
2987
3022
  /******************************************************************************/
2988
3023
 
2989
- logger.resize = (function() {
2990
- let timer;
2991
-
2992
- const resize = function() {
2993
- const vrect = dom.body.getBoundingClientRect();
2994
- for ( const elem of qsa$('.vscrollable') ) {
2995
- const crect = elem.getBoundingClientRect();
2996
- const dh = crect.bottom - vrect.bottom;
2997
- if ( dh === 0 ) { continue; }
2998
- elem.style.height = Math.ceil(crect.height - dh) + 'px';
2999
- }
3000
- };
3001
-
3002
- const resizeAsync = function() {
3003
- if ( timer !== undefined ) { return; }
3004
- timer = self.requestAnimationFrame(( ) => {
3005
- timer = undefined;
3006
- resize();
3007
- });
3008
- };
3009
-
3010
- resizeAsync();
3011
-
3012
- dom.on(window, 'resize', resizeAsync, { passive: true });
3013
-
3014
- return resizeAsync;
3015
- })();
3016
-
3017
- /******************************************************************************/
3018
-
3019
3024
  const grabView = function() {
3020
3025
  if ( logger.ownerId === undefined ) {
3021
3026
  logger.ownerId = Date.now();
@@ -3043,11 +3048,6 @@ dom.on('#pageSelector', 'change', pageSelectorChanged);
3043
3048
  dom.on('#netInspector .vCompactToggler', 'click', toggleVCompactView);
3044
3049
  dom.on('#pause', 'click', pauseNetInspector);
3045
3050
 
3046
- dom.on('#logLevel', 'click', ev => {
3047
- const level = dom.cl.toggle(ev.currentTarget, 'active') ? 2 : 1;
3048
- broadcast({ what: 'loggerLevelChanged', level });
3049
- });
3050
-
3051
3051
  dom.on('#netInspector #vwContent', 'copy', ev => {
3052
3052
  const selection = document.getSelection();
3053
3053
  const text = selection.toString();