Skip to content

Commit e33364f

Browse files
Release build 4.11.2 [ci release]
1 parent 1cbf542 commit e33364f

File tree

30 files changed

+1031
-334
lines changed

30 files changed

+1031
-334
lines changed

.eslintrc

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
{
2-
"extends": "standard",
2+
"extends": ["standard", "plugin:@typescript-eslint/recommended"],
33
"root": true,
44
"parserOptions": {
55
"ecmaVersion": "latest",
6+
"project": "./tsconfig.json"
67
},
8+
"parser": "@typescript-eslint/parser",
9+
"plugins": ["promise","@typescript-eslint"],
710
"globals": {
811
"$USER_PREFERENCES$": "readonly",
912
"$USER_UNPROTECTED_DOMAINS$": "readonly",
@@ -12,7 +15,10 @@
1215
"$BUNDLED_CONFIG$": "readonly"
1316
},
1417
"rules": {
15-
"indent": ["error", 4]
18+
"indent": ["error", 4],
19+
"require-await": ["error"],
20+
"promise/prefer-await-to-then": ["error"],
21+
"@typescript-eslint/await-thenable": "error"
1622
},
1723
"env": {
1824
"webextensions": true,

Sources/ContentScopeScripts/dist/contentScope.js

Lines changed: 125 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -430,19 +430,77 @@
430430
/**
431431
* @typedef {object} Platform
432432
* @property {'ios' | 'macos' | 'extension' | 'android' | 'windows'} name
433-
* @property {string} [version]
433+
* @property {string | number } [version]
434434
*/
435435

436436
/**
437437
* @typedef {object} UserPreferences
438438
* @property {Platform} platform
439439
* @property {boolean} [debug]
440440
* @property {boolean} [globalPrivacyControl]
441+
* @property {number} [versionNumber] - Android version number only
442+
* @property {string} [versionString] - Non Android version string
441443
* @property {string} sessionKey
442444
*/
443445

444446
/**
445-
* @param {{ features: Record<string, { state: string; settings: any; exceptions: string[] }>; unprotectedTemporary: string; }} data
447+
* Expansion point to add platform specific versioning logic
448+
* @param {UserPreferences} preferences
449+
* @returns {string | number | undefined}
450+
*/
451+
function getPlatformVersion (preferences) {
452+
if (preferences.versionNumber) {
453+
return preferences.versionNumber
454+
}
455+
if (preferences.versionString) {
456+
return preferences.versionString
457+
}
458+
return undefined
459+
}
460+
461+
function parseVersionString (versionString) {
462+
const [major = 0, minor = 0, patch = 0] = versionString.split('.').map(Number);
463+
return {
464+
major,
465+
minor,
466+
patch
467+
}
468+
}
469+
470+
/**
471+
* @param {string} minVersionString
472+
* @param {string} applicationVersionString
473+
* @returns {boolean}
474+
*/
475+
function satisfiesMinVersion (minVersionString, applicationVersionString) {
476+
const { major: minMajor, minor: minMinor, patch: minPatch } = parseVersionString(minVersionString);
477+
const { major, minor, patch } = parseVersionString(applicationVersionString);
478+
479+
return (major > minMajor ||
480+
(major >= minMajor && minor > minMinor) ||
481+
(major >= minMajor && minor >= minMinor && patch >= minPatch))
482+
}
483+
484+
/**
485+
* @param {string | number | undefined} minSupportedVersion
486+
* @param {string | number | undefined} currentVersion
487+
* @returns {boolean}
488+
*/
489+
function isSupportedVersion (minSupportedVersion, currentVersion) {
490+
if (typeof currentVersion === 'string' && typeof minSupportedVersion === 'string') {
491+
if (satisfiesMinVersion(minSupportedVersion, currentVersion)) {
492+
return true
493+
}
494+
} else if (typeof currentVersion === 'number' && typeof minSupportedVersion === 'number') {
495+
if (minSupportedVersion <= currentVersion) {
496+
return true
497+
}
498+
}
499+
return false
500+
}
501+
502+
/**
503+
* @param {{ features: Record<string, { state: string; settings: any; exceptions: string[], minSupportedVersion?: string|number }>; unprotectedTemporary: string[]; }} data
446504
* @param {string[]} userList
447505
* @param {UserPreferences} preferences
448506
* @param {string[]} platformSpecificFeatures
@@ -452,13 +510,25 @@
452510
const allowlisted = userList.filter(domain => domain === topLevelHostname).length > 0;
453511
const remoteFeatureNames = Object.keys(data.features);
454512
const platformSpecificFeaturesNotInRemoteConfig = platformSpecificFeatures.filter((featureName) => !remoteFeatureNames.includes(featureName));
513+
/** @type {Record<string, any>} */
514+
const output = { ...preferences };
515+
if (output.platform) {
516+
const version = getPlatformVersion(preferences);
517+
if (version) {
518+
output.platform.version = version;
519+
}
520+
}
455521
const enabledFeatures = remoteFeatureNames.filter((featureName) => {
456522
const feature = data.features[featureName];
523+
// Check that the platform supports minSupportedVersion checks and that the feature has a minSupportedVersion
524+
if (feature.minSupportedVersion && preferences.platform?.version) {
525+
if (!isSupportedVersion(feature.minSupportedVersion, preferences.platform.version)) {
526+
return false
527+
}
528+
}
457529
return feature.state === 'enabled' && !isUnprotectedDomain(topLevelHostname, feature.exceptions)
458530
}).concat(platformSpecificFeaturesNotInRemoteConfig); // only disable platform specific features if it's explicitly disabled in remote config
459531
const isBroken = isUnprotectedDomain(topLevelHostname, data.unprotectedTemporary);
460-
/** @type {Record<string, any>} */
461-
const output = { ...preferences };
462532
output.site = {
463533
domain: topLevelHostname,
464534
isBroken,
@@ -518,7 +588,8 @@
518588
'fingerprintingTemporaryStorage',
519589
'navigatorInterface',
520590
'clickToLoad',
521-
'elementHiding'
591+
'elementHiding',
592+
'exceptionHandler'
522593
];
523594

524595
/**
@@ -577,6 +648,7 @@
577648
case './features/click-to-play.js': return Promise.resolve().then(function () { return clickToPlay; });
578649
case './features/cookie.js': return Promise.resolve().then(function () { return cookie; });
579650
case './features/element-hiding.js': return Promise.resolve().then(function () { return elementHiding; });
651+
case './features/exception-handler.js': return Promise.resolve().then(function () { return exceptionHandler; });
580652
case './features/fingerprinting-audio.js': return Promise.resolve().then(function () { return fingerprintingAudio; });
581653
case './features/fingerprinting-battery.js': return Promise.resolve().then(function () { return fingerprintingBattery; });
582654
case './features/fingerprinting-canvas.js': return Promise.resolve().then(function () { return fingerprintingCanvas; });
@@ -624,14 +696,15 @@
624696
/**
625697
* @param {LoadArgs} args
626698
*/
627-
async function load (args) {
699+
function load (args) {
628700
const mark = performanceMonitor.mark('load');
629701
if (!shouldRun()) {
630702
return
631703
}
632704

633705
for (const featureName of featureNames) {
634706
const filename = featureName.replace(/([a-zA-Z])(?=[A-Z0-9])/g, '$1-').toLowerCase();
707+
// eslint-disable-next-line promise/prefer-await-to-then
635708
const feature = __variableDynamicImportRuntime0__(`./features/${filename}.js`).then((exported) => {
636709
const ContentFeature = exported.default;
637710
const featureInstance = new ContentFeature(featureName);
@@ -2273,6 +2346,7 @@
22732346
})
22742347
}
22752348

2349+
// eslint-disable-next-line @typescript-eslint/no-empty-function
22762350
init (args) {
22772351
}
22782352

@@ -2285,6 +2359,7 @@
22852359
this.measure();
22862360
}
22872361

2362+
// eslint-disable-next-line @typescript-eslint/no-empty-function
22882363
load (args) {
22892364
}
22902365

@@ -2302,11 +2377,12 @@
23022377
}
23032378
}
23042379

2380+
// eslint-disable-next-line @typescript-eslint/no-empty-function
23052381
update () {
23062382
}
23072383
}
23082384

2309-
// @ts-nocheck
2385+
// TODO - Remove these comments to enable full linting.
23102386

23112387
let devMode$1 = false;
23122388
let isYoutubePreviewsEnabled$1 = false;
@@ -2530,13 +2606,13 @@
25302606
}
25312607

25322608
/*
2533-
* Fades out the given element. Returns a promise that resolves when the fade is complete.
2534-
* @param {Element} element - the element to fade in or out
2535-
* @param {int} interval - frequency of opacity updates (ms)
2536-
* @param {bool} fadeIn - true if the element should fade in instead of out
2537-
*/
2609+
* Fades out the given element. Returns a promise that resolves when the fade is complete.
2610+
* @param {Element} element - the element to fade in or out
2611+
* @param {int} interval - frequency of opacity updates (ms)
2612+
* @param {boolean} fadeIn - true if the element should fade in instead of out
2613+
*/
25382614
fadeElement (element, interval, fadeIn) {
2539-
return new Promise((resolve, reject) => {
2615+
return new Promise(resolve => {
25402616
let opacity = fadeIn ? 0 : 1;
25412617
const originStyle = element.style.cssText;
25422618
const fadeOut = setInterval(function () {
@@ -2560,7 +2636,7 @@
25602636

25612637
clickFunction (originalElement, replacementElement) {
25622638
let clicked = false;
2563-
const handleClick = async function handleClick (e) {
2639+
const handleClick = function handleClick (e) {
25642640
// Ensure that the click is created by a user event & prevent double clicks from adding more animations
25652641
if (e.isTrusted && !clicked) {
25662642
this.isUnblocked = true;
@@ -2633,15 +2709,13 @@
26332709
parent.replaceChild(fbContainer, replacementElement);
26342710
fbContainer.appendChild(replacementElement);
26352711
fadeIn.appendChild(fbElement);
2636-
fbElement.addEventListener('load', () => {
2637-
this.fadeOutElement(replacementElement)
2638-
.then(v => {
2639-
fbContainer.replaceWith(fbElement);
2640-
this.dispatchEvent(fbElement, 'ddg-ctp-placeholder-clicked');
2641-
this.fadeInElement(fadeIn).then(() => {
2642-
fbElement.focus(); // focus on new element for screen readers
2643-
});
2644-
});
2712+
fbElement.addEventListener('load', async () => {
2713+
await this.fadeOutElement(replacementElement);
2714+
fbContainer.replaceWith(fbElement);
2715+
this.dispatchEvent(fbElement, 'ddg-ctp-placeholder-clicked');
2716+
await this.fadeInElement(fadeIn);
2717+
// Focus on new element for screen readers.
2718+
fbElement.focus();
26452719
}, { once: true });
26462720
// Note: This event only fires on Firefox, on Chrome the frame's
26472721
// load event will always fire.
@@ -2840,7 +2914,7 @@
28402914
* @typedef unblockClickToLoadContentRequest
28412915
* @property {string} entity
28422916
* The entity to unblock requests for (e.g. "Facebook, Inc.").
2843-
* @property {bool} [isLogin=false]
2917+
* @property {boolean} [isLogin=false]
28442918
* True if we should "allow social login", defaults to false.
28452919
* @property {string} action
28462920
* The Click to Load blocklist rule action (e.g. "block-ctl-fb") that should
@@ -2853,7 +2927,7 @@
28532927
* Send a message to the background to unblock requests for the given entity for
28542928
* the page.
28552929
* @param {unblockClickToLoadContentRequest} message
2856-
* @see {@event ddg-ctp-unblockClickToLoadContent-complete} for the response handler.
2930+
* @see {@link ddg-ctp-unblockClickToLoadContent-complete} for the response handler.
28572931
*/
28582932
function unblockClickToLoadContent$1 (message) {
28592933
sendMessage('unblockClickToLoadContent', message);
@@ -6185,13 +6259,13 @@
61856259
}
61866260

61876261
getExpiry () {
6188-
// @ts-ignore
6262+
// @ts-expect-error expires is not defined in the type definition
61896263
if (!this.maxAge && !this.expires) {
61906264
return NaN
61916265
}
61926266
const expiry = this.maxAge
61936267
? parseInt(this.maxAge)
6194-
// @ts-ignore
6268+
// @ts-expect-error expires is not defined in the type definition
61956269
: (new Date(this.expires) - new Date()) / 1000;
61966270
return expiry
61976271
}
@@ -6745,6 +6819,28 @@
67456819
default: ElementHiding
67466820
});
67476821

6822+
class ExceptionHandler extends ContentFeature {
6823+
init () {
6824+
// Report to the debugger panel if an uncaught exception occurs
6825+
function handleUncaughtException (e) {
6826+
postDebugMessage('jsException', {
6827+
documentUrl: document.location.href,
6828+
message: e.message,
6829+
filename: e.filename,
6830+
lineno: e.lineno,
6831+
colno: e.colno,
6832+
stack: e.error.stack
6833+
});
6834+
}
6835+
globalThis.addEventListener('error', handleUncaughtException);
6836+
}
6837+
}
6838+
6839+
var exceptionHandler = /*#__PURE__*/Object.freeze({
6840+
__proto__: null,
6841+
default: ExceptionHandler
6842+
});
6843+
67486844
// @ts-nocheck
67496845
const sjcl = (() => {
67506846
/*jslint indent: 2, bitwise: false, nomen: false, plusplus: false, white: false, regexp: false */
@@ -8968,6 +9064,7 @@
89689064
try {
89699065
defineProperty(globalThis, property, {
89709066
get: () => value,
9067+
// eslint-disable-next-line @typescript-eslint/no-empty-function
89719068
set: () => {},
89729069
configurable: true
89739070
});
@@ -10279,6 +10376,7 @@
1027910376
return Promise.reject(new DOMException('Pan-tilt-zoom is not supported'))
1028010377
}
1028110378

10379+
// eslint-disable-next-line promise/prefer-await-to-then
1028210380
return DDGReflect.apply(target, thisArg, args).then(function (stream) {
1028310381
console.debug(`User stream ${stream.id} has been acquired`);
1028410382
userMediaStreams.add(stream);

build-output.eslintrc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
{
22
"root": true,
3-
"plugins": ["@typescript-eslint"],
3+
"plugins": ["promise", "@typescript-eslint"],
44
"parserOptions": {
55
"ecmaVersion": "latest",
66
},
77
"rules": {
88
"no-implicit-globals": "error",
9-
"typescript-eslint/ban-ts-comment": "off"
9+
"typescript-eslint/ban-ts-comment": "off",
10+
"promise/prefer-await-to-then": "off",
1011
}
1112
}

0 commit comments

Comments
 (0)